]> git.proxmox.com Git - mirror_frr.git/commitdiff
zebra: optionally use protobuf with FPM
authorAvneesh Sachdev <avneesh@sproute.com>
Mon, 4 Apr 2016 17:54:58 +0000 (10:54 -0700)
committerDonald Sharp <sharpd@cumulusnetworks.com>
Fri, 23 Sep 2016 16:12:16 +0000 (12:12 -0400)
Change zebra so that it can optionally use protobuf serialization when
communicating with a Forwarding Plane Manager component.

  * zebra/main.c

    Add the --fpm-format/-F command line option. This allows the user
    to control the format (protbuf|netlink) that is used to
    communicate with the FPM.

  * zebra/zebra_fpm.c

    - zebra_init_msg_format(),

      This new function is invoked on process startup to determine the
      FPM format that should be used.

    - zfpm_init()

      Change to accept any 'FPM message format' specified by the user
      (via the new command line flag).

    - zebra_encode_route()

      Tweak to use the selected FPM format.

  * zebra_fpm_protobuf.c

    New code to build protobuf messages to be sent to the FPM.

  * zebra/Makefile.am

    - Include common.am

    - Build new file zebra_fpm_protobuf.c when protobuf is available.

    - Link with the fpm_pb library.

Signed-off-by: Avneesh Sachdev <avneesh@sproute.com>
zebra/Makefile.am
zebra/main.c
zebra/zebra_fpm.c
zebra/zebra_fpm.h
zebra/zebra_fpm_private.h
zebra/zebra_fpm_protobuf.c [new file with mode: 0644]

index 851e597796dfe9ee3b50adbc379fb66c32bcc588..26c283b9c107cb59dd206d9117265e9073b1bdc3 100644 (file)
@@ -1,3 +1,5 @@
+include ../common.am
+
 ## Process this file with automake to produce Makefile.in.
 
 AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib
@@ -21,6 +23,10 @@ if HAVE_NETLINK
 othersrc = zebra_fpm_netlink.c
 endif
 
+if HAVE_PROTOBUF
+protobuf_srcs = zebra_fpm_protobuf.c
+endif
+
 AM_CFLAGS = $(WERROR)
 
 sbin_PROGRAMS = zebra
@@ -33,7 +39,8 @@ zebra_SOURCES = \
        redistribute.c debug.c rtadv.c zebra_snmp.c zebra_vty.c \
        irdp_main.c irdp_interface.c irdp_packet.c router-id.c zebra_fpm.c \
        $(othersrc) zebra_ptm.c zebra_rnh.c zebra_ptm_redistribute.c \
-       zebra_ns.c zebra_vrf.c zebra_static.c zebra_mpls.c zebra_mpls_vty.c
+       zebra_ns.c zebra_vrf.c zebra_static.c zebra_mpls.c zebra_mpls_vty.c \
+       $(protobuf_srcs)
 
 testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \
        zebra_vty.c zebra_ptm.c zebra_routemap.c zebra_ns.c zebra_vrf.c \
@@ -49,7 +56,7 @@ noinst_HEADERS = \
        zebra_ptm_redistribute.h zebra_ptm.h zebra_routemap.h \
        zebra_ns.h zebra_vrf.h ioctl_solaris.h zebra_static.h zebra_mpls.h
 
-zebra_LDADD = $(otherobj) ../lib/libzebra.la $(LIBCAP)
+zebra_LDADD = $(otherobj) ../lib/libzebra.la $(LIBCAP) $(Q_FPM_PB_CLIENT_LDOPTS)
 
 testzebra_LDADD = ../lib/libzebra.la $(LIBCAP)
 
index faa6cdb317b27d51776f90426dd3bf9c8e35131c..da7e6b6fb8e907e25b3ffe74f957ec02d0e2830f 100644 (file)
@@ -83,6 +83,7 @@ struct option longopts[] =
   { "daemon",       no_argument,       NULL, 'd'},
   { "allow_delete", no_argument,       NULL, 'a'},
   { "keep_kernel",  no_argument,       NULL, 'k'},
+  { "fpm_format",   required_argument, NULL, 'F'},
   { "config_file",  required_argument, NULL, 'f'},
   { "pid_file",     required_argument, NULL, 'i'},
   { "socket",       required_argument, NULL, 'z'},
@@ -143,6 +144,7 @@ usage (char *progname, int status)
              "-d, --daemon       Runs in daemon mode\n"\
              "-a, --allow_delete Allow other processes to delete Quagga Routes\n" \
              "-f, --config_file  Set configuration file name\n"\
+             "-F, --fpm_format   Set fpm format to 'netlink' or 'protobuf'\n"\
              "-i, --pid_file     Set process identifier file name\n"\
              "-z, --socket       Set path of zebra socket\n"\
              "-k, --keep_kernel  Don't delete old routes which installed by "\
@@ -238,6 +240,7 @@ main (int argc, char **argv)
   char *progname;
   struct thread thread;
   char *zserv_path = NULL;
+  char *fpm_format = NULL;
 
   /* Set umask before anything for security */
   umask (0027);
@@ -257,9 +260,9 @@ main (int argc, char **argv)
       int opt;
   
 #ifdef HAVE_NETLINK  
-      opt = getopt_long (argc, argv, "bdakf:i:z:hA:P:ru:g:vs:C", longopts, 0);
+      opt = getopt_long (argc, argv, "bdakf:F:i:z:hA:P:ru:g:vs:C", longopts, 0);
 #else
-      opt = getopt_long (argc, argv, "bdakf:i:z:hA:P:ru:g:vC", longopts, 0);
+      opt = getopt_long (argc, argv, "bdakf:F:i:z:hA:P:ru:g:vC", longopts, 0);
 #endif /* HAVE_NETLINK */
 
       if (opt == EOF)
@@ -286,6 +289,9 @@ main (int argc, char **argv)
        case 'f':
          config_file = optarg;
          break;
+       case 'F':
+         fpm_format = optarg;
+         break;
        case 'A':
          vty_addr = optarg;
          break;
@@ -377,9 +383,9 @@ main (int argc, char **argv)
 #endif /* HAVE_SNMP */
 
 #ifdef HAVE_FPM
-  zfpm_init (zebrad.master, 1, 0);
+  zfpm_init (zebrad.master, 1, 0, fpm_format);
 #else
-  zfpm_init (zebrad.master, 0, 0);
+  zfpm_init (zebrad.master, 0, 0, fpm_format);
 #endif
 
   /* Process the configuration file. Among other configuration
index 34f0068514239c82da3115ae157df070f6cc4cbf..5a68bcbfaca2f71505b1db56915703bc3eb884e6 100644 (file)
@@ -141,6 +141,15 @@ typedef enum {
 
 } zfpm_state_t;
 
+/*
+ * Message format to be used to communicate with the FPM.
+ */
+typedef enum
+{
+  ZFPM_MSG_FORMAT_NONE,
+  ZFPM_MSG_FORMAT_NETLINK,
+  ZFPM_MSG_FORMAT_PROTOBUF,
+} zfpm_msg_format_e;
 /*
  * Globals.
  */
@@ -152,6 +161,11 @@ typedef struct zfpm_glob_t_
    */
   int enabled;
 
+  /*
+   * Message format to be used to communicate with the fpm.
+   */
+  zfpm_msg_format_e message_format;
+
   struct thread_master *master;
 
   zfpm_state_t state;
@@ -866,19 +880,40 @@ zfpm_writes_pending (void)
  */
 static inline int
 zfpm_encode_route (rib_dest_t *dest, struct rib *rib, char *in_buf,
-                  size_t in_buf_len)
+                  size_t in_buf_len, fpm_msg_type_e *msg_type)
 {
-#ifndef HAVE_NETLINK
-  return 0;
-#else
-
+  size_t len;
   int cmd;
+  len = 0;
 
-  cmd = rib ? RTM_NEWROUTE : RTM_DELROUTE;
+  *msg_type = FPM_MSG_TYPE_NONE;
 
-  return zfpm_netlink_encode_route (cmd, dest, rib, in_buf, in_buf_len);
+  switch (zfpm_g->message_format) {
 
+  case ZFPM_MSG_FORMAT_PROTOBUF:
+#ifdef HAVE_PROTOBUF
+    len = zfpm_protobuf_encode_route (dest, rib, (uint8_t *) in_buf,
+                                     in_buf_len);
+    *msg_type = FPM_MSG_TYPE_PROTOBUF;
+#endif
+    break;
+
+  case ZFPM_MSG_FORMAT_NETLINK:
+#ifdef HAVE_NETLINK
+    *msg_type = FPM_MSG_TYPE_NETLINK;
+    cmd = rib ? RTM_NEWROUTE : RTM_DELROUTE;
+    len = zfpm_netlink_encode_route (cmd, dest, rib, in_buf, in_buf_len);
+    assert(fpm_msg_align(len) == len);
+    *msg_type = FPM_MSG_TYPE_NETLINK;
 #endif /* HAVE_NETLINK */
+    break;
+
+  default:
+    break;
+  }
+
+  return len;
+
 }
 
 /*
@@ -886,7 +921,7 @@ zfpm_encode_route (rib_dest_t *dest, struct rib *rib, char *in_buf,
  *
  * Returns the rib that is to be sent to the FPM for a given dest.
  */
-static struct rib *
+struct rib *
 zfpm_route_for_update (rib_dest_t *dest)
 {
   struct rib *rib;
@@ -922,6 +957,7 @@ zfpm_build_updates (void)
   fpm_msg_hdr_t *hdr;
   struct rib *rib;
   int is_add, write_msg;
+  fpm_msg_type_e msg_type;
 
   s = zfpm_g->obuf;
 
@@ -946,7 +982,6 @@ zfpm_build_updates (void)
 
     hdr = (fpm_msg_hdr_t *) buf;
     hdr->version = FPM_PROTO_VERSION;
-    hdr->msg_type = FPM_MSG_TYPE_NETLINK;
 
     data = fpm_msg_data (hdr);
 
@@ -966,11 +1001,13 @@ zfpm_build_updates (void)
       }
 
     if (write_msg) {
-      data_len = zfpm_encode_route (dest, rib, (char *) data, buf_end - data);
+      data_len = zfpm_encode_route (dest, rib, (char *) data, buf_end - data,
+                                   &msg_type);
 
       assert (data_len);
       if (data_len)
        {
+         hdr->msg_type = msg_type;
          msg_len = fpm_data_len_to_msg_len (data_len);
          hdr->msg_len = htons (msg_len);
          stream_forward_endp (s, msg_len);
@@ -1572,6 +1609,64 @@ DEFUN ( no_fpm_remote_ip,
 }
 
 
+/*
+ * zfpm_init_message_format
+ */
+static inline void
+zfpm_init_message_format (const char *format)
+{
+  int have_netlink, have_protobuf;
+
+  have_netlink = have_protobuf = 0;
+
+#ifdef HAVE_NETLINK
+  have_netlink = 1;
+#endif
+
+#ifdef HAVE_PROTOBUF
+  have_protobuf = 1;
+#endif
+
+  zfpm_g->message_format = ZFPM_MSG_FORMAT_NONE;
+
+  if (!format)
+    {
+      if (have_netlink)
+       {
+         zfpm_g->message_format = ZFPM_MSG_FORMAT_NETLINK;
+       }
+      else if (have_protobuf)
+       {
+         zfpm_g->message_format = ZFPM_MSG_FORMAT_PROTOBUF;
+       }
+      return;
+    }
+
+  if (!strcmp ("netlink", format))
+    {
+      if (!have_netlink)
+       {
+         zlog_err ("FPM netlink message format is not available");
+         return;
+       }
+      zfpm_g->message_format = ZFPM_MSG_FORMAT_NETLINK;
+      return;
+    }
+
+  if (!strcmp ("protobuf", format))
+    {
+      if (!have_protobuf)
+       {
+         zlog_err ("FPM protobuf message format is not available");
+         return;
+       }
+      zfpm_g->message_format = ZFPM_MSG_FORMAT_PROTOBUF;
+      return;
+    }
+
+  zlog_warn ("Unknown fpm format '%s'", format);
+}
+
 /**
  * fpm_remote_srv_write 
  *
@@ -1601,11 +1696,13 @@ int fpm_remote_srv_write (struct vty *vty )
  *
  * @param[in] port port at which FPM is running.
  * @param[in] enable TRUE if the zebra FPM module should be enabled
+ * @param[in] format to use to talk to the FPM. Can be 'netink' or 'protobuf'.
  *
  * Returns TRUE on success.
  */
 int
-zfpm_init (struct thread_master *master, int enable, uint16_t port)
+zfpm_init (struct thread_master *master, int enable, uint16_t port,
+          const char *format)
 {
   static int initialized = 0;
 
@@ -1621,16 +1718,6 @@ zfpm_init (struct thread_master *master, int enable, uint16_t port)
   zfpm_g->sock = -1;
   zfpm_g->state = ZFPM_STATE_IDLE;
 
-  /*
-   * Netlink must currently be available for the Zebra-FPM interface
-   * to be enabled.
-   */
-#ifndef HAVE_NETLINK
-  enable = 0;
-#endif
-
-  zfpm_g->enabled = enable;
-
   zfpm_stats_init (&zfpm_g->stats);
   zfpm_stats_init (&zfpm_g->last_ivl_stats);
   zfpm_stats_init (&zfpm_g->cumulative_stats);
@@ -1640,6 +1727,16 @@ zfpm_init (struct thread_master *master, int enable, uint16_t port)
   install_element (CONFIG_NODE, &fpm_remote_ip_cmd);
   install_element (CONFIG_NODE, &no_fpm_remote_ip_cmd);
 
+  zfpm_init_message_format(format);
+
+  /*
+   * Disable FPM interface if no suitable format is available.
+   */
+  if (zfpm_g->message_format == ZFPM_MSG_FORMAT_NONE)
+      enable = 0;
+
+  zfpm_g->enabled = enable;
+
   if (!enable) {
     return 1;
   }
index 7fa71e40bca464ba79be95e36bf3672b6c81068d..fdb069965bdca798ddae8b9a085f80bbe69725f0 100644 (file)
@@ -28,7 +28,8 @@
 /*
  * Externs.
  */
-extern int zfpm_init (struct thread_master *master, int enable, uint16_t port);
+extern int zfpm_init (struct thread_master *master, int enable, uint16_t port,
+                     const char *message_format);
 extern void zfpm_trigger_update (struct route_node *rn, const char *reason);
 extern int fpm_remote_srv_write (struct vty *vty);
 
index 809a70a445969544222f259383d3d7268392f61a..1c4fd4c22f5f2b953bcba45ea9a396e08b01cd7e 100644 (file)
@@ -53,4 +53,9 @@ extern int
 zfpm_netlink_encode_route (int cmd, rib_dest_t *dest, struct rib *rib,
                           char *in_buf, size_t in_buf_len);
 
+extern int
+zfpm_protobuf_encode_route (rib_dest_t *dest, struct rib *rib,
+                           uint8_t *in_buf, size_t in_buf_len);
+
+extern struct rib *zfpm_route_for_update (rib_dest_t *dest);
 #endif /* _ZEBRA_FPM_PRIVATE_H */
diff --git a/zebra/zebra_fpm_protobuf.c b/zebra/zebra_fpm_protobuf.c
new file mode 100644 (file)
index 0000000..beef310
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * zebra_fpm_protobuf.c
+ *
+ * @copyright Copyright (C) 2016 Sproute Networks, Inc.
+ *
+ * @author Avneesh Sachdev <avneesh@sproute.com>
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Quagga; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#include <zebra.h>
+
+#include "log.h"
+#include "rib.h"
+
+#include "qpb/qpb.pb-c.h"
+#include "qpb/qpb.h"
+#include "qpb/qpb_allocator.h"
+#include "qpb/linear_allocator.h"
+#include "fpm/fpm_pb.h"
+
+#include "zebra_fpm_private.h"
+
+/*
+ * create_delete_route_message
+ */
+static Fpm__DeleteRoute *
+create_delete_route_message (qpb_allocator_t *allocator, rib_dest_t *dest,
+                            struct rib *rib)
+{
+  Fpm__DeleteRoute *msg;
+
+  msg = QPB_ALLOC(allocator, typeof(*msg));
+  if (!msg) {
+    assert(0);
+    return NULL;
+  }
+
+  fpm__delete_route__init(msg);
+  msg->vrf_id = rib_dest_vrf(dest)->vrf_id;
+
+  qpb_address_family_set(&msg->address_family, rib_dest_af(dest));
+
+  /*
+   * XXX Hardcode subaddress family for now.
+   */
+  msg->sub_address_family = QPB__SUB_ADDRESS_FAMILY__UNICAST;
+  msg->key = fpm_route_key_create (allocator, rib_dest_prefix(dest));
+  if (!msg->key) {
+    assert(0);
+    return NULL;
+  }
+
+  return msg;
+}
+
+/*
+ * add_nexthop
+ */
+static inline int
+add_nexthop (qpb_allocator_t *allocator, Fpm__AddRoute *msg, rib_dest_t *dest,
+            struct nexthop *nexthop)
+{
+  uint32_t if_index;
+  union g_addr *gateway, *src;
+
+  gateway = src = NULL;
+
+  if_index = nexthop->ifindex;
+
+  if (nexthop->type == NEXTHOP_TYPE_IPV4
+      || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
+    {
+      gateway = &nexthop->gate;
+      if (nexthop->src.ipv4.s_addr)
+       src = &nexthop->src;
+    }
+
+  if (nexthop->type == NEXTHOP_TYPE_IPV6
+      || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
+      || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
+    {
+      gateway = &nexthop->gate;
+    }
+
+  if (nexthop->type == NEXTHOP_TYPE_IFINDEX
+      || nexthop->type == NEXTHOP_TYPE_IFNAME)
+    {
+      if (nexthop->src.ipv4.s_addr)
+       src = &nexthop->src;
+    }
+
+  if (!gateway && if_index == 0)
+    return 0;
+
+  /*
+   * We have a valid nexthop.
+   */
+  {
+    Fpm__Nexthop *pb_nh;
+    pb_nh = QPB_ALLOC(allocator, typeof(*pb_nh));
+    if (!pb_nh) {
+      assert(0);
+      return 0;
+    }
+
+    fpm__nexthop__init(pb_nh);
+
+    if (if_index != 0) {
+      pb_nh->if_id = qpb_if_identifier_create (allocator, if_index);
+    }
+
+    if (gateway) {
+      pb_nh->address = qpb_l3_address_create (allocator, gateway,
+                                             rib_dest_af(dest));
+    }
+
+    msg->nexthops[msg->n_nexthops++] = pb_nh;
+  }
+
+  // TODO: Use src.
+
+  return 1;
+}
+
+/*
+ * create_add_route_message
+ */
+static Fpm__AddRoute *
+create_add_route_message (qpb_allocator_t *allocator, rib_dest_t *dest,
+                         struct rib *rib)
+{
+  Fpm__AddRoute *msg;
+  int discard;
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
+  uint num_nhs, u;
+  struct nexthop *nexthops[MAX (MULTIPATH_NUM, 64)];
+
+  msg = QPB_ALLOC(allocator, typeof(*msg));
+  if (!msg) {
+    assert(0);
+    return NULL;
+  }
+
+  fpm__add_route__init(msg);
+
+  msg->vrf_id = rib_dest_vrf(dest)->vrf_id;
+
+  qpb_address_family_set (&msg->address_family, rib_dest_af(dest));
+
+  /*
+   * XXX Hardcode subaddress family for now.
+   */
+  msg->sub_address_family = QPB__SUB_ADDRESS_FAMILY__UNICAST;
+  msg->key = fpm_route_key_create (allocator, rib_dest_prefix(dest));
+  qpb_protocol_set (&msg->protocol, rib->type);
+
+  if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) || (rib->flags & ZEBRA_FLAG_REJECT))
+    discard = 1;
+  else
+    discard = 0;
+
+  if (discard)
+    {
+      if (rib->flags & ZEBRA_FLAG_BLACKHOLE) {
+       msg->route_type = FPM__ROUTE_TYPE__BLACKHOLE;
+      } else if (rib->flags & ZEBRA_FLAG_REJECT) {
+       msg->route_type = FPM__ROUTE_TYPE__UNREACHABLE;
+      } else {
+       assert (0);
+      }
+      return msg;
+    }
+  else {
+    msg->route_type = FPM__ROUTE_TYPE__NORMAL;
+  }
+
+  msg->metric = rib->metric;
+
+  /*
+   * Figure out the set of nexthops to be added to the message.
+   */
+  num_nhs = 0;
+  for (ALL_NEXTHOPS_RO (rib->nexthop, nexthop, tnexthop, recursing))
+    {
+      if (MULTIPATH_NUM != 0 && num_nhs >= MULTIPATH_NUM)
+        break;
+
+      if (num_nhs >= ZEBRA_NUM_OF(nexthops))
+       break;
+
+      if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+        continue;
+
+      if (!CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+       continue;
+
+      nexthops[num_nhs] = nexthop;
+      num_nhs++;
+    }
+
+  if (!num_nhs) {
+    zfpm_debug ("netlink_encode_route(): No useful nexthop.");
+    assert(0);
+    return NULL;
+  }
+
+  /*
+   * And add them to the message.
+   */
+  if (!(msg->nexthops = qpb_alloc_ptr_array(allocator, num_nhs))) {
+    assert(0);
+    return NULL;
+  }
+
+  msg->n_nexthops = 0;
+  for (u = 0; u < num_nhs; u++) {
+    if (!add_nexthop(allocator, msg, dest, nexthops[u])) {
+      assert(0);
+      return NULL;
+    }
+  }
+
+  assert(msg->n_nexthops == num_nhs);
+
+  return msg;
+}
+
+/*
+ * create_route_message
+ */
+static Fpm__Message *
+create_route_message (qpb_allocator_t *allocator, rib_dest_t *dest,
+                     struct rib *rib)
+{
+  Fpm__Message *msg;
+
+  msg = QPB_ALLOC(allocator, typeof(*msg));
+  if (!msg) {
+    assert(0);
+    return NULL;
+  }
+
+  fpm__message__init(msg);
+
+  if (!rib) {
+    msg->type = FPM__MESSAGE__TYPE__DELETE_ROUTE;
+    msg->delete_route = create_delete_route_message(allocator, dest, rib);
+    if (!msg->delete_route) {
+      assert(0);
+      return NULL;
+    }
+    return msg;
+  }
+
+  msg->type = FPM__MESSAGE__TYPE__ADD_ROUTE;
+  msg->add_route = create_add_route_message(allocator, dest, rib);
+  if (!msg->add_route) {
+    assert(0);
+    return NULL;
+  }
+
+  return msg;
+}
+
+/*
+ * zfpm_protobuf_encode_route
+ *
+ * Create a protobuf message corresponding to the given route in the
+ * given buffer space.
+ *
+ * Returns the number of bytes written to the buffer. 0 or a negative
+ * value indicates an error.
+ */
+int
+zfpm_protobuf_encode_route (rib_dest_t *dest, struct rib *rib,
+                           uint8_t *in_buf, size_t in_buf_len)
+{
+  Fpm__Message *msg;
+  QPB_DECLARE_STACK_ALLOCATOR (allocator, 4096);
+  size_t len;
+
+  QPB_INIT_STACK_ALLOCATOR (allocator);
+
+  msg = create_route_message(&allocator, dest, rib);
+  if (!msg) {
+    assert(0);
+    return 0;
+  }
+
+  len = fpm__message__pack(msg, (uint8_t *) in_buf);
+  assert(len <= in_buf_len);
+
+  QPB_RESET_STACK_ALLOCATOR (allocator);
+  return len;
+}