]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
SNMP: Context support and improved registering
authorVojtech Vilimek <vojtech.vilimek@nic.cz>
Mon, 11 Sep 2023 11:06:20 +0000 (13:06 +0200)
committerVojtech Vilimek <vojtech.vilimek@nic.cz>
Mon, 11 Sep 2023 11:41:39 +0000 (13:41 +0200)
proto/snmp/bgp_mib.c
proto/snmp/bgp_mib.h
proto/snmp/config.Y
proto/snmp/snmp.c
proto/snmp/snmp.h
proto/snmp/snmp_utils.c
proto/snmp/snmp_utils.h
proto/snmp/subagent.c
proto/snmp/subagent.h

index 073b3c5ddd31184f1bef95cac07f6425a6941da7..c9912bca9d53ac5618b0f1f45cc425ac7b093ec1 100644 (file)
@@ -13,6 +13,8 @@
 #include "subagent.h"
 #include "bgp_mib.h"
 
+static inline void ip4_to_oid(struct oid *oid, ip4_addr addr);
+
 /* BGP_MIB states see enum BGP_INTERNAL_STATES */
 static const char * const debug_bgp_states[] UNUSED = {
   [BGP_INTERNAL_INVALID]                = "BGP_INTERNAL_INVALID",
@@ -164,15 +166,16 @@ snmp_bgp_register(struct snmp_proto *p)
 {
   snmp_log("snmp_bgp_register()");
 
-  u32 bgp_mib_prefix[] = {1, 15, 1};
+  //u32 bgp_mib_prefix[] = {1, 15, 1};
+  u32 bgp_mib_prefix[] = { 1, 15 };
 
   {
     /* Register the whole BGP4-MIB::bgp root tree node */
     struct snmp_register *registering = snmp_register_create(p, SNMP_BGP4_MIB);
 
     struct oid *oid = mb_alloc(p->p.pool, snmp_oid_sizeof(2));
-    put_u8(&oid->n_subid, 2);
-    put_u8(&oid->prefix, 2);
+    STORE_U8(oid->n_subid, 2);
+    STORE_U8(oid->prefix, SNMP_MGMT);
 
     memcpy(oid->ids, bgp_mib_prefix, 2 * sizeof(u32));
 
@@ -180,9 +183,37 @@ snmp_bgp_register(struct snmp_proto *p)
     add_tail(&p->register_queue, &registering->n);
     p->register_to_ack++;
 
-    /* snmp_register(struct snmp_proto *p, struct oid *oid, uint index, uint len, u8 is_instance) */
-    snmp_register(p, oid, 0, 1, 0);
+    /* snmp_register(struct snmp_proto *p, struct oid *oid, uint index, uint len, u8 is_instance, uint contid) */
+    snmp_register(p, oid, 1, 0, SNMP_REGISTER_TREE, SNMP_DEFAULT_CONTEXT);
   }
+
+  u32 bgp_peer_entry[] = { 1, 15, 3, 1, 1 };
+  u32 bound = 24;
+  HASH_WALK(p->bgp_hash, next, peer)
+  {
+    if (peer->flags & SNMP_BGP_P_REGISTERED)
+      continue;
+
+    struct bgp_proto *bgp = (struct bgp_proto *) peer->config->c.proto;
+
+    struct snmp_register *registering = snmp_register_create(p, SNMP_BGP4_MIB);
+
+    struct oid *oid = mb_alloc(p->p.pool, snmp_oid_sizeof(9));
+    STORE_U8(oid->n_subid, 9);
+    STORE_U8(oid->prefix, SNMP_MGMT);
+
+    for (uint i = 0; i < sizeof(bgp_peer_entry)/sizeof(bgp_peer_entry[0]); i++)
+      STORE_U32(oid->ids[i], bgp_peer_entry[i]);
+    ip4_to_oid(oid, ipa_to_ip4(bgp->remote_ip));
+
+    /* index is position of x in .1.3.6.1.2.15.3.1.x (1-based) */
+    snmp_register(p, oid, bound, 9, SNMP_REGISTER_INSTANCE, peer->context_id);
+
+    registering->oid = oid;
+    add_tail(&p->register_queue, &registering->n);
+    p->register_to_ack++;
+  }
+  HASH_WALK_END;
 }
 
 static int
@@ -244,8 +275,19 @@ ip4_from_oid(const struct oid *o)
   );
 }
 
+static inline void
+ip4_to_oid(struct oid *o, ip4_addr addr)
+{
+  u32 tmp = ip4_to_u32(addr);
+  ASSUME(o->n_subid >= 9);
+  STORE_U32(o->ids[5], (tmp & 0xFF000000) >> 24);
+  STORE_U32(o->ids[6], (tmp & 0x00FF0000) >> 16);
+  STORE_U32(o->ids[7], (tmp & 0x0000FF00) >>  8);
+  STORE_U32(o->ids[8], (tmp & 0x000000FF) >>  0);
+}
+
 static void
-print_bgp_record(struct bgp_config *config)
+print_bgp_record(const struct bgp_config *config)
 {
   struct proto_config *cf = (struct proto_config *) config;
   struct bgp_proto *bgp_proto = (struct bgp_proto *) cf->proto;
@@ -937,7 +979,7 @@ snmp_bgp_search(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, ui
 
 static byte *
 bgp_fill_dynamic(struct snmp_proto UNUSED *p, struct agentx_varbind *vb,
-                struct snmp_pdu_context *c, u8 state)
+                struct snmp_pdu *c, u8 state)
 {
   struct oid *oid = &vb->name;
   uint size = c->size - snmp_varbind_header_size(vb);
@@ -1202,7 +1244,7 @@ temp, temp, pkt);
 
 void
 snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind *vb,
-             struct snmp_pdu_context *c)
+             struct snmp_pdu *c)
 {
   u8 state = snmp_bgp_state(&vb->name);
 
index d22234981e93a3b69178f5b712345b51f9898952..750b168c1a27704ef6a89c65b77e4b8769fce003 100644 (file)
@@ -45,7 +45,7 @@ u8 snmp_bgp_getnext_valid(u8 state);
 
 struct oid *snmp_bgp_search(struct snmp_proto *p, struct oid *o_start, struct oid *o_end, uint contid);
 enum snmp_search_res snmp_bgp_search2(struct snmp_proto *p, struct oid **searched, const struct oid *o_end, uint contid);
-void snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind *vb, struct snmp_pdu_context *c);
+void snmp_bgp_fill(struct snmp_proto *p, struct agentx_varbind *vb, struct snmp_pdu *c);
 
 void snmp_bgp_notify_established(struct snmp_proto *p, struct bgp_proto *bgp);
 void snmp_bgp_notify_backward_trans(struct snmp_proto *p, struct bgp_proto *bgp);
index 3d9fc122fa348e5add72c966bb9588f8b07118a5..b71a9850ed96f00fca4d4517e18da1ea5692da0e 100644 (file)
@@ -16,10 +16,12 @@ CF_DEFINES
 
 #define SNMP_CFG ((struct snmp_config *) this_proto)
 
+struct snmp_bond *this_bond = NULL;
+
 CF_DECLS
 
 CF_KEYWORDS(SNMP, PROTOCOL, BPG, LOCAL, AS, REMOTE, ADDRESS, PORT, DESCRIPTION,
-           TIMEOUT, PRIORITY)
+           TIMEOUT, PRIORITY, CONTEXT, DEFAULT)
 
 CF_GRAMMAR
 
@@ -66,6 +68,8 @@ snmp_proto_start: proto_start SNMP
 
   init_list(&SNMP_CFG->bgp_entries);
   SNMP_CFG->bonds = 0;
+  /* We always have the default context */
+  SNMP_CFG->contexts = 1;
 
   SNMP_CFG->local_ip = IPA_NONE;
   SNMP_CFG->remote_ip = ipa_build4(127,0,0,1);
@@ -80,18 +84,61 @@ snmp_proto_start: proto_start SNMP
 
 proto_name ;
 
+snmp_context:
+   /* empty */   {
+    if (!this_bond)
+    {
+      log(L_INFO "snmp_context no string alloc");
+      this_bond = cfg_alloc(sizeof(struct snmp_bond));
+    }
 
-snmp_bgp_bond: BGP symbol
+    this_bond->context = NULL;
+  }
+ | CONTEXT DEFAULT {
+    if (!this_bond)
+    {
+      log(L_INFO "snmp_context CONTEXT DEFAULT alloc");
+      this_bond = cfg_alloc(sizeof(struct snmp_bond));
+    }
+    this_bond->context = NULL;
+  }
+ | CONTEXT text {
+    if(!this_bond)
+    {
+      log(L_INFO "snmp_context CONTEXT text alloc");
+      this_bond = cfg_alloc(sizeof(struct snmp_bond));
+    }
+    this_bond->context = $2;
+    SNMP_CFG->contexts++;
+    log(L_INFO "storing context %s to bond at 0x%p", $2, this_bond);
+  }
+ ;
+
+snmp_bgp_bond: BGP symbol snmp_context
 {
-  struct snmp_bond *this_bond = cfg_alloc(sizeof(struct snmp_bond));
+  /* the snmp_context rule sets the correct value of this_bond */
+  log(L_INFO "this_bond (at 0x%p) has value %s", this_bond,
+    (this_bond->context) ? this_bond->context : "<no_val>");
+  if (!this_bond)
+  {
+    log(L_INFO "snmp_bgp_bond BGP symbol ... alloc");
+    this_bond = cfg_alloc(sizeof(struct snmp_bond));
+    log(L_INFO "Unexpedted alloc in snmp_bgp_bond rule");
+  }
+  else
+  {
+    log(L_INFO, "working with this_bond (at 0x%p)", this_bond);
+  }
   this_bond->type = SNMP_BGP;
 
   cf_assert_symbol($2, SYM_PROTO);
   this_bond->proto = $2->proto;
 
   if (!this_bond->proto) cf_error("BGP protocol %s not found", $2->name);
-  add_tail(&SNMP_CFG->bgp_entries, NODE this_bond);
+  add_tail(&SNMP_CFG->bgp_entries, &this_bond->n);
   SNMP_CFG->bonds++;
+
+  this_bond = NULL;
 }
 
 CF_CODE
index 02532d3d0a0f4f730dc7fbadc31422916a4be637..33fa0d932812dccf972ca02a4d4b5191239c9e7d 100644 (file)
@@ -108,9 +108,13 @@ snmp_init(struct proto_config *CF)
 
   p->timeout = cf->timeout;
 
-  snmp_log("snmp_reconfigure() lip: %I:%u rip: %I:%u",
+  snmp_log("snmp_init() lip: %I:%u rip: %I:%u",
     cf->local_ip, cf->local_port, cf->remote_ip, cf->remote_port);
 
+  /* used when assigning the context ids in s_cont_create() */
+  p->context_max = 1;
+  p->context_id_map = NULL;
+
   return P;
 }
 
@@ -130,7 +134,30 @@ snmp_cleanup(struct snmp_proto *p)
   rfree(p->lock);
   p->lock = NULL;
 
-  // TODO cleanup lists, hash table, trie, ...
+  p->partial_response = NULL;
+
+  struct snmp_register *r, *r2;
+  WALK_LIST_DELSAFE(r, r2, p->register_queue)
+  {
+    rem_node(&r->n);
+    mb_free(r);
+    r = NULL;
+  }
+
+  struct snmp_registered_oid *ro, *ro2;
+  WALK_LIST_DELSAFE(ro, ro2, p->bgp_registered)
+  {
+    rem_node(&r->n);
+    mb_free(ro);
+    ro = NULL;
+  }
+
+  HASH_FREE(p->bgp_hash);
+  HASH_FREE(p->context_hash);
+  mb_free(p->context_id_map);
+  p->context_id_map = NULL;
+
+  // TODO cleanup trie
 
   return (p->state = SNMP_DOWN);
 }
@@ -312,16 +339,29 @@ snmp_start(struct proto *P)
 
   /* We create copy of bonds to BGP protocols. */
   HASH_INIT(p->bgp_hash, p->p.pool, 10);
+  HASH_INIT(p->context_hash, p->p.pool, 10);
+
+  /* We always have at least the default context */
+  p->context_id_map = mb_allocz(p->p.pool, cf->contexts * sizeof(struct snmp_context *));
+  log(L_INFO "number of context allocated %d", cf->contexts);
+
+  struct snmp_context *defaultc = mb_alloc(p->p.pool, sizeof(struct snmp_context));
+  defaultc->context = "";
+  defaultc->context_id = 0;
+  defaultc->flags = 0; /* TODO Default context fl. */
+  HASH_INSERT(p->context_hash, SNMP_H_CONTEXT, defaultc);
+
+  p->context_id_map[0] = defaultc;
 
   struct snmp_bond *b;
   WALK_LIST(b, cf->bgp_entries)
   {
-    struct bgp_config *bc = (struct bgp_config *) b->proto;
+    const struct bgp_config *bc = (struct bgp_config *) b->proto;
     if (bc && !ipa_zero(bc->remote_ip))
     {
       struct snmp_bgp_peer *peer = \
        mb_allocz(p->p.pool, sizeof(struct snmp_bgp_peer));
-      peer->config = (struct bgp_config *) b->proto;
+      peer->config = bc;
       peer->peer_ip = bc->remote_ip;
 
       struct net_addr net;
@@ -330,9 +370,30 @@ snmp_start(struct proto *P)
       trie_add_prefix(p->bgp_trie, &net, IP4_MAX_PREFIX_LENGTH, IP4_MAX_PREFIX_LENGTH);
 
       HASH_INSERT(p->bgp_hash, SNMP_HASH, peer);
+
+      /* Handle non-default context */
+      if (b->context)
+      {
+       const struct snmp_context *c = snmp_cont_create(p, b->context);
+       snmp_log("creating snmp context %s with id %u, writing", b->context, c->context_id);
+       p->context_id_map[c->context_id] = c;
+       peer->context_id = c->context_id;
+      }
     }
   }
 
+  {
+    u32 *ptr = mb_alloc(p->p.pool, 4 * sizeof(u32));
+    *ptr = 1;
+    ptr[2] = 4;
+    (void)ptr[1]; (void)ptr[0]; (void)ptr[2];
+    mb_free(ptr);
+    log(L_INFO "testing alloc 3");
+  }
+
+  snmp_log("values of context cf %u  proto %u", cf->contexts, p->context_max);
+  ASSUME(cf->contexts == p->context_max);
+
   snmp_startup(p);
   return PS_START;
 }
@@ -344,6 +405,27 @@ snmp_reconfigure(struct proto *P, struct proto_config *CF)
   const struct snmp_config *new = SKIP_BACK(struct snmp_config, cf, CF);
   const struct snmp_config *old = SKIP_BACK(struct snmp_config, cf, p->p.cf);
 
+  struct snmp_bond *b1, *b2;
+  WALK_LIST(b1, new->bgp_entries)
+  {
+    WALK_LIST(b2, old->bgp_entries)
+    {
+      if (!strcmp(b1->proto->name, b2->proto->name))
+       goto skip;
+
+      /* Both bonds use default context */
+      if (!b1->context && !b2->context)
+       goto skip;
+
+      /* Both bonds use same non-default context */
+      if (b1->context && b2->context && !strcmp(b1->context, b2->context))
+       goto skip;
+    }
+
+    return 0;
+skip:;
+  }
+
   return !memcmp(((byte *) old) + sizeof(struct proto_config),
       ((byte *) new) + sizeof(struct proto_config),
       OFFSETOF(struct snmp_config, description) - sizeof(struct proto_config))
@@ -391,7 +473,7 @@ snmp_show_proto_info(struct proto *P)
     cli_msg(-1006, "    in total: %u", bp->stats.rx_messages);
     cli_msg(-1006, "    out total: %u", bp->stats.tx_messages);
     cli_msg(-1006, "    fsm transitions: %u",
-bp->stats.fsm_established_transitions);
+      bp->stats.fsm_established_transitions);
 
     cli_msg(-1006, "    fsm total time: -- (0)");
     cli_msg(-1006, "    retry interval: %u", bcf->connect_retry_time);
index 96f71611c8a9f749b6f60295503852cf33290664..5dbfd7063351976c734bfd0097863cae8b166832 100644 (file)
@@ -51,10 +51,17 @@ enum snmp_proto_state {
 /* hash table only store ip4 addresses */
 #define SNMP_HASH_LESS(ip1, ip2) SNMP_HASH_LESS4(ip1,ip2)
 
+/* context hash table macros */
+#define SNMP_H_CONTEXT_KEY(c)          c->context
+#define SNMP_H_CONTEXT_NEXT(c)         c->next
+#define SNMP_H_CONTEXT_EQ(s1,s2)       strcmp(s1,s2)
+#define SNMP_H_CONTEXT_FN(s)           mem_hash(s, strlen(s))
+
 struct snmp_bond {
   node n;
   struct proto_config *proto;
   u8 type;
+  const char *context;
 };
 
 struct snmp_config {
@@ -67,15 +74,21 @@ struct snmp_config {
   u8 timeout;
   u8 priority;
   //struct iface *iface;
+  u32 bonds;
+  uint contexts; /* Number of all conetexts including the default */
   const char *description;
   list bgp_entries;
-  u32 bonds;
   // TODO add support for subagent oid identification
 };
 
+#define SNMP_BGP_P_REGISTERING 0x01
+#define SNMP_BGP_P_REGISTERED  0x02
+
 struct snmp_bgp_peer {
-  struct bgp_config *config;
+  const struct bgp_config *config;
   ip_addr peer_ip;
+  uint context_id;
+  u8 flags;
   struct snmp_bgp_peer *next;
 };
 
@@ -93,6 +106,14 @@ struct snmp_registered_oid {
   struct oid *oid;
 };
 
+struct snmp_context {
+  const char *context; /* string name */
+  //uint length; /* strlen() of name */
+  uint context_id;
+  u8 flags;
+  struct snmp_context *next;
+};
+
 struct snmp_proto {
   struct proto p;
   struct object_lock *lock;
@@ -121,6 +142,9 @@ struct snmp_proto {
   // map
   struct f_trie *bgp_trie;
   HASH(struct snmp_bgp_peer) bgp_hash;
+  HASH(struct snmp_context) context_hash;
+  const struct snmp_context **context_id_map;
+  uint context_max;
   struct tbf rl_gen;
 
   timer *ping_timer;
index b4eb41df7c85b439b3dada3c7b28d80f20956f6d..c678f89da92cf2ac3b349f09cca30af80077095a 100644 (file)
@@ -149,11 +149,13 @@ snmp_varbind_size(struct agentx_varbind *vb, int byte_ord)
   return hdr_size + snmp_str_size_from_len(LOAD_PTR(data, byte_ord));
 }
 
+/*
 inline uint
 snmp_context_size(struct agentx_context *c)
 {
   return (c && c->length) ? snmp_str_size_from_len(c->length) : 0;
 }
+*/
 
 struct agentx_varbind *
 snmp_create_varbind(byte *buf, struct oid *oid)
@@ -208,14 +210,13 @@ snmp_put_nstr(byte *buf, const char *str, uint len)
 {
   uint alen = BIRD_ALIGN(len, 4);
 
-  // TODO check for '\0' in the str bytes?
   STORE_PTR(buf, len);
   buf += 4;
   memcpy(buf, str, len);
 
   /* Insert zero padding in the gap at the end */
   for (uint i = 0; i < alen - len; i++)
-    buf[len + i] = 0x00;
+    buf[len + i] = '\0';
 
   return buf + alen;
 }
@@ -415,7 +416,7 @@ snmp_register_same(struct snmp_register *r, struct agentx_header *h, u8 class)
 }
 
 void
-snmp_register_ack(struct snmp_proto *p, struct agentx_header *h)
+snmp_register_ack(struct snmp_proto *p, struct agentx_header *h, u8 class)
 {
   snmp_log("snmp_register_ack()");
 
@@ -428,7 +429,7 @@ h->packet_id);
     // TODO add support for more mib trees (other than BGP)
     snmp_log("checking registration request sid: %u tid: %u pid: %u",
       reg->session_id, reg->transaction_id, reg->packet_id);
-    if (snmp_register_same(reg, h, SNMP_BGP4_MIB))
+    if (snmp_register_same(reg, h, class))
     {
       struct snmp_registered_oid *ro = \
         mb_alloc(p->p.pool, sizeof(struct snmp_registered_oid));
@@ -575,3 +576,48 @@ snmp_search_res_to_type(enum snmp_search_res r)
 
   return type_arr[r];
 }
+
+inline const struct snmp_context *
+snmp_cont_find(struct snmp_proto *p, const char *name)
+{
+  u32 *ptr = mb_alloc(p->p.pool, 4 * sizeof(u32));
+  *ptr = 1;
+  ptr[2] = 4;
+  (void)ptr[1]; (void)ptr[0]; (void)ptr[2];
+  mb_free(ptr);
+  return HASH_FIND(p->context_hash, SNMP_H_CONTEXT, name);
+}
+
+inline const struct snmp_context *
+snmp_cont_get(struct snmp_proto *p, uint id)
+{
+  if (id >= p->context_max)
+    return NULL;
+
+  return p->context_id_map[id];
+}
+
+inline const struct snmp_context *
+snmp_cont_create(struct snmp_proto *p, const char *name)
+{
+  const struct snmp_context *c = snmp_cont_find(p, name);
+
+  if (c)
+    return c;
+
+  struct snmp_context *c2;
+  c2 = mb_alloc(p->p.pool, sizeof(struct snmp_context));
+  c2->context = name;
+  c2->context_id = p->context_max++;
+  c2->flags = 0;
+
+  u32 *ptr = mb_alloc(p->p.pool, 4 * sizeof(u32));
+  *ptr = 1;
+  ptr[2] = 4;
+  (void)ptr[1]; (void)ptr[0]; (void)ptr[2];
+  mb_free(ptr);
+
+  HASH_INSERT(p->context_hash, SNMP_H_CONTEXT, c2);
+
+  return c2;
+}
index ba38cd250930d7b6aca6dbf8e56130fcbd487b8c..95fec87010154349943c010b92b379012a551334 100644 (file)
@@ -14,7 +14,7 @@ size_t snmp_oid_sizeof(uint n_subid);
 uint snmp_varbind_hdr_size_from_oid(struct oid *oid);
 uint snmp_varbind_header_size(struct agentx_varbind *vb);
 uint snmp_varbind_size(struct agentx_varbind *vb, int byte_ord);
-uint snmp_context_size(struct agentx_context *c);
+//uint snmp_context_size(struct agentx_context *c);
 
 void snmp_oid_copy(struct oid *dest, const struct oid *src);
 
@@ -46,7 +46,7 @@ void snmp_oid_dump(struct oid *oid);
 
 struct snmp_register *snmp_register_create(struct snmp_proto *p, u8 mib_class);
 
-void snmp_register_ack(struct snmp_proto *p, struct agentx_header *h);
+void snmp_register_ack(struct snmp_proto *p, struct agentx_header *h, u8 class);
 
 byte *snmp_varbind_int(struct agentx_varbind *vb, uint size, u32 val);
 byte *snmp_varbind_counter32(struct agentx_varbind *vb, uint size, u32 val);
@@ -57,7 +57,13 @@ byte *snmp_varbind_nstr(struct agentx_varbind *vb, uint size, const char *str, u
 
 void snmp_dump_packet(byte *pkt, uint size);
 
+const struct snmp_context *snmp_cont_find(struct snmp_proto *p, const char *name);
+const struct snmp_context *snmp_cont_get(struct snmp_proto *p, uint context_id);
+const struct snmp_context *snmp_cont_create(struct snmp_proto *p, const char *name);
+
 enum agentx_type snmp_search_res_to_type(enum snmp_search_res res);
 
 int agentx_type_size(enum agentx_type t);
+
+
 #endif
index 5da528e62cb4d5a240b4172cff8831bbb3d7bd1e..75839d809bc46a5b7897036f3049ef8828be7bb1 100644 (file)
  *
  */
 
-static void snmp_mib_fill2(struct snmp_proto *p, struct oid *oid, struct snmp_pdu_context *c);
+static void snmp_mib_fill2(struct snmp_proto *p, struct oid *oid, struct snmp_pdu *c);
 static uint parse_response(struct snmp_proto *p, byte *buf, uint size);
 static void do_response(struct snmp_proto *p, byte *buf, uint size);
 static uint parse_gets2_pdu(struct snmp_proto *p, byte *buf, uint size, uint *skip);
 static uint parse_close_pdu(struct snmp_proto *p, byte *buf, uint size);
-static struct agentx_response *prepare_response(struct snmp_proto *p, struct snmp_pdu_context *c);
+static struct agentx_response *prepare_response(struct snmp_proto *p, struct snmp_pdu *c);
 static void response_err_ind(struct agentx_response *res, uint err, uint ind);
 static uint update_packet_size(struct snmp_proto *p, byte *start, byte *end);
-static struct oid *search_mib(struct snmp_proto *p, const struct oid *o_start, const struct oid *o_end, struct oid *o_curr, struct snmp_pdu_context *c, enum snmp_search_res *result);
+static struct oid *search_mib(struct snmp_proto *p, const struct oid *o_start, const struct oid *o_end, struct oid *o_curr, struct snmp_pdu *c, enum snmp_search_res *result);
 
 u32 snmp_internet[] = { SNMP_ISO, SNMP_ORG, SNMP_DOD, SNMP_INTERNET };
 
@@ -83,7 +83,7 @@ open_pdu(struct snmp_proto *p, struct oid *oid)
   const struct snmp_config *cf = SKIP_BACK(struct snmp_config, cf, p->p.cf);
   sock *sk = p->sock;
 
-  struct snmp_pdu_context c = SNMP_PDU_CONTEXT(sk);
+  struct snmp_pdu c = SNMP_PDU_CONTEXT(sk);
   byte *buf = c.buffer;
 
   /* +4 for timeout (1B with 4B alignment) */
@@ -94,6 +94,7 @@ open_pdu(struct snmp_proto *p, struct oid *oid)
     buf = c.buffer;
   }
 
+  /* Function open_pdu() does not generate agentx_pkt. */
   struct agentx_header *h = (struct agentx_header *) c.buffer;
   ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
   SNMP_BLANK_HEADER(h, AGENTX_OPEN_PDU);
@@ -123,7 +124,7 @@ snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, in
 {
   sock *sk = p->sock;
 
-  struct snmp_pdu_context c = SNMP_PDU_CONTEXT(sk);
+  struct snmp_pdu c = SNMP_PDU_CONTEXT(sk);
 
 #define UPTIME_SIZE \
   (6 * sizeof(u32)) /* sizeof( { u32 vb_type, u32 oid_hdr, u32 ids[4] } )*/
@@ -142,6 +143,7 @@ snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, in
   struct agentx_header *h = (struct agentx_header *) c.buffer;
   ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
   SNMP_BLANK_HEADER(h, AGENTX_NOTIFY_PDU);
+  p->packet_id++;
   SNMP_SESSION(h, p);
   c.byte_ord = h->flags & AGENTX_NETWORK_BYTE_ORDER;
 
@@ -183,7 +185,6 @@ snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, in
   ADVANCE(c.buffer, c.size, size);
 
   uint s = update_packet_size(p, sk->tbuf, c.buffer);
-
   int ret = sk_send(sk, s);
   if (ret > 0)
     snmp_log("sk_send OK!");
@@ -227,15 +228,24 @@ de_allocate_pdu(struct snmp_proto *p, struct oid *oid, u8 type)
 
 /* Register-PDU / Unregister-PDU */
 static void
-un_register_pdu(struct snmp_proto *p, struct oid *oid, uint index, uint len, u8 type, u8 is_instance)
+un_register_pdu(struct snmp_proto *p, struct oid *oid, uint len, uint index, u8 type, u8 is_instance, uint contid)
 {
   const struct snmp_config *cf = SKIP_BACK(struct snmp_config, cf, p->p.cf);
   sock *sk = p->sock;
-  struct snmp_pdu_context c = SNMP_PDU_CONTEXT(sk);
+  struct snmp_pdu c = SNMP_PDU_CONTEXT(sk);
   byte *buf = c.buffer;
 
   /* conditional +4 for upper-bound (optinal field) */
-  if (c.size < AGENTX_HEADER_SIZE + snmp_oid_size(oid) + ((len > 1) ? 4 : 0))
+  uint sz = AGENTX_HEADER_SIZE + snmp_oid_size(oid) + ((len > 1) ? 4 : 0);
+
+  const struct snmp_context *sc = NULL;
+  if (contid)
+  {
+    sc = snmp_cont_get(p, contid);
+    sz += snmp_str_size(sc->context);
+  }
+
+  if (c.size < sz)
   {
     snmp_log("un_register_pdu() insufficient size");
     snmp_manage_tbuf(p, &c);
@@ -243,22 +253,28 @@ un_register_pdu(struct snmp_proto *p, struct oid *oid, uint index, uint len, u8
   }
 
   snmp_log("un_register_pdu()");
-  struct agentx_un_register_pdu *ur = (struct agentx_un_register_pdu *)c.buffer;
-  ADVANCE(c.buffer, c.size, sizeof(struct agentx_un_register_pdu));
-  struct agentx_header *h = &ur->h;
+  struct agentx_header *h = (struct agentx_header *) c.buffer;
+  ADVANCE(c.buffer, c.size, AGENTX_HEADER_SIZE);
 
   SNMP_HEADER(h, type, is_instance ? AGENTX_FLAG_INSTANCE_REGISTRATION : 0);
-  /* use new transactionID, reset packetID */
   p->packet_id++;
   SNMP_SESSION(h, p);
   c.byte_ord = h->flags & AGENTX_NETWORK_BYTE_ORDER;
 
+  log("un_register_pdu contid %u s_cont (at 0x%p) %s", contid, sc, (sc &&
+      sc->context) ? sc->context : "<not_avail>");
+
+  SNMP_NON_DEFAULT_CONTEXT(h, c, contid);
+
+  struct agentx_un_register_hdr *ur = (struct agentx_un_register_hdr *) c.buffer;
+
   /* do not override timeout */
   STORE_U8(ur->timeout, p->timeout);
   /* default priority */
   STORE_U8(ur->priority, cf->priority);
   STORE_U8(ur->range_subid, (len > 1) ? index : 0);
   STORE_U8(ur->pad, 0);
+  ADVANCE(c.buffer, c.size, sizeof(struct agentx_un_register_hdr));
 
   snmp_put_oid(c.buffer, oid);
   ADVANCE(c.buffer, c.size, snmp_oid_size(oid));
@@ -284,23 +300,23 @@ un_register_pdu(struct snmp_proto *p, struct oid *oid, uint index, uint len, u8
 
 /* Register-PDU */
 void
-snmp_register(struct snmp_proto *p, struct oid *oid, uint index, uint len, u8 is_instance)
+snmp_register(struct snmp_proto *p, struct oid *oid, uint len, uint index, u8 is_instance, uint contid)
 {
-  un_register_pdu(p, oid, index, len, AGENTX_REGISTER_PDU, is_instance);
+  un_register_pdu(p, oid, len, index, AGENTX_REGISTER_PDU, is_instance, contid);
 }
 
 /* Unregister-PDU */
 void UNUSED
-snmp_unregister(struct snmp_proto *p, struct oid *oid, uint index, uint len)
+snmp_unregister(struct snmp_proto *p, struct oid *oid, uint len, uint index, uint contid)
 {
-  un_register_pdu(p, oid, index, len, AGENTX_UNREGISTER_PDU, 0);
+  un_register_pdu(p, oid, len, index, AGENTX_UNREGISTER_PDU, 0, contid);
 }
 
 static void
 close_pdu(struct snmp_proto *p, u8 reason)
 {
   sock *sk = p->sock;
-  struct snmp_pdu_context c = SNMP_PDU_CONTEXT(sk);
+  struct snmp_pdu c = SNMP_PDU_CONTEXT(sk);
   byte *buf = c.buffer;
 
   snmp_log("close_pdu() size: %u %c %u", c.size, (c.size > AGENTX_HEADER_SIZE + 4)
@@ -475,13 +491,13 @@ refresh_ids(struct snmp_proto *p, struct agentx_header *h)
  * @size: number of packet bytes in buffer
  * retval number of byte parsed
  *
- * function parse_ptk() parses response-pdu and calls do_response().
+ * function parse_pkt() parses response-pdu and calls do_response().
  * returns number of bytes parsed by function excluding size of header.
  */
 static uint
 parse_pkt(struct snmp_proto *p, byte *pkt, uint size, uint *skip)
 {
-  snmp_log("parse_ptk() pkt start: %p", pkt);
+  snmp_log("parse_pkt() pkt start: %p", pkt);
 
   if (size < AGENTX_HEADER_SIZE)
     return 0;
@@ -540,7 +556,8 @@ parse_response(struct snmp_proto *p, byte *res, uint size)
 
   uint pkt_size = LOAD_U32(h->payload, byte_ord);
   snmp_log("p_res pkt_size %u", pkt_size);
-  if (size < pkt_size + AGENTX_HEADER_SIZE) {
+  if (size < pkt_size + AGENTX_HEADER_SIZE)
+  {
     snmp_log("parse_response early return");
     return 0;
   }
@@ -562,13 +579,6 @@ parse_response(struct snmp_proto *p, byte *res, uint size)
   return pkt_size + AGENTX_HEADER_SIZE;
 }
 
-static inline int
-snmp_registered_all(struct snmp_proto *p)
-{
-  snmp_log("snmp_registered_all() %u", list_length(&p->register_queue));
-  return p->register_to_ack == 0;
-}
-
 static void
 snmp_register_mibs(struct snmp_proto *p)
 {
@@ -580,7 +590,7 @@ snmp_register_mibs(struct snmp_proto *p)
 }
 
 static void
-do_response(struct snmp_proto *p, byte *buf, uint size UNUSED)
+do_response(struct snmp_proto *p, byte *buf, uint size)
 {
   snmp_log("do_response()");
   struct agentx_response *r = (void *) buf;
@@ -607,14 +617,28 @@ do_response(struct snmp_proto *p, byte *buf, uint size UNUSED)
       p->state = SNMP_REGISTER;
       snmp_register_mibs(p);
       snmp_log("do_response state SNMP_INIT register list %u", list_length(&p->register_queue));
-
       break;
 
     case SNMP_REGISTER:
       snmp_log("do_response state SNMP_REGISTER register list %u", list_length(&p->register_queue));
-      snmp_register_ack(p ,h);
 
-      if (snmp_registered_all(p)) {
+      byte *pkt = buf;
+      ADVANCE(pkt, size, AGENTX_HEADER_SIZE);
+
+      uint clen;
+      const char *context;
+      SNMP_LOAD_CONTEXT((struct agentx_header *) buf, pkt, context, clen);
+
+      if (size < snmp_str_size_from_len(clen))
+       return;
+
+      ADVANCE(pkt, size, snmp_str_size_from_len(clen));
+      const struct oid *oid = (void *) pkt;
+
+      snmp_register_ack(p, h, snmp_get_mib_class(oid));
+
+      if (p->register_to_ack == 0)
+      {
        snmp_log("changing proto_snmp state to CONNECTED");
        p->state = SNMP_CONN;
        proto_notify_state(&p->p, PS_UP);
@@ -622,11 +646,9 @@ do_response(struct snmp_proto *p, byte *buf, uint size UNUSED)
       break;
 
     case SNMP_CONN:
-      // proto_notify_state(&p->p, PS_UP);
       break;
 
     case SNMP_STOP:
-      snmp_down(p);
       break;
 
     default:
@@ -653,7 +675,7 @@ snmp_get_mib_class(const struct oid *oid)
 
 static void
 snmp_get_next2(struct snmp_proto *p, struct oid *o_start, struct oid *o_end,
-              struct snmp_pdu_context *c)
+              struct snmp_pdu *c)
 {
   snmp_log("get_next2()");
   enum snmp_search_res r;
@@ -721,7 +743,7 @@ snmp_get_next2(struct snmp_proto *p, struct oid *o_start, struct oid *o_end,
 
 static void
 snmp_get_bulk2(struct snmp_proto *p, struct oid *o_start, struct oid *o_end,
-              struct agentx_bulk_state *state, struct snmp_pdu_context *c)
+              struct agentx_bulk_state *state, struct snmp_pdu *c)
 {
   if (state->index <= state->getbulk.non_repeaters)
   {
@@ -834,6 +856,7 @@ response_err_ind(struct agentx_response *res, uint err, uint ind)
 static uint
 parse_gets2_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, uint *skip)
 {
+  // TODO checks for c.size underflow
   snmp_log("parse_gets2_pdu()");
 
   struct oid *o_start = NULL, *o_end = NULL;
@@ -844,15 +867,15 @@ parse_gets2_pdu(struct snmp_proto *p, byte * const pkt_start, uint size, uint *s
   uint pkt_size = LOAD_U32(h->payload, h->flags & AGENTX_NETWORK_BYTE_ORDER);
 
   sock *sk = p->sock;
-  struct snmp_pdu_context c = SNMP_PDU_CONTEXT(sk);
+  struct snmp_pdu c = SNMP_PDU_CONTEXT(sk);
   // TODO better handling of endianness
   c.byte_ord = 0; /* use little-endian */
 
   uint clen;     /* count of characters in context (without last '\0') */
-  char *context;  /* newly allocated string of character */
+  const char *context;  /* pointer to RX-buffer context */
 
   /* alters pkt; assign context, clen */
-  SNMP_LOAD_CONTEXT(p, h, pkt, context, clen);
+  SNMP_LOAD_CONTEXT(h, pkt, context, clen);
 
   /*
    * We need more data; for valid response we need to know full
@@ -1040,7 +1063,6 @@ send:
 
   p->partial_response = NULL;
 
-  mb_free(context);
   mb_free(o_start);
   mb_free(o_end);
 
@@ -1064,9 +1086,9 @@ partial:
 
 
 wait:
-  mb_free(context);
   mb_free(o_start);
   mb_free(o_end);
+  p->packet_id--; /* we did not use the packetID */
 
   return 0;
 }
@@ -1163,7 +1185,7 @@ snmp_ping(struct snmp_proto *p)
   sock *sk = p->sock;
   snmp_dump_packet(sk->tpos, AGENTX_HEADER_SIZE + 4);
   snmp_log("snmp_ping sk->tpos 0x%p", sk->tpos);
-  struct snmp_pdu_context c = SNMP_PDU_CONTEXT(sk);
+  struct snmp_pdu c = SNMP_PDU_CONTEXT(sk);
 
   if (c.size < AGENTX_HEADER_SIZE)
     snmp_manage_tbuf(p, &c);
@@ -1180,6 +1202,7 @@ snmp_ping(struct snmp_proto *p)
   snmp_dump_packet(sk->tpos, AGENTX_HEADER_SIZE + 4);
   /* sending only header -> pkt - buf */
   uint s = update_packet_size(p, sk->tpos, c.buffer);
+
   int ret = sk_send(sk, s);
   if (ret > 0)
     snmp_log("sk_send OK!");
@@ -1231,7 +1254,7 @@ int snmp_search_check_end_oid(const struct oid *found, const struct oid *bound)
    working only with o_start, o_end allocated in heap (not from buffer)*/
 static struct oid *
 search_mib(struct snmp_proto *p, const struct oid *o_start, const struct oid *o_end,
-          struct oid *o_curr, struct snmp_pdu_context *c,
+          struct oid *o_curr, struct snmp_pdu *c,
           enum snmp_search_res *result)
 {
   snmp_log("search_mib()");
@@ -1344,16 +1367,13 @@ snmp_prefixize(struct snmp_proto *proto, const struct oid *oid, int byte_ord)
 
 static void
 snmp_mib_fill2(struct snmp_proto *p, struct oid *oid,
-              struct snmp_pdu_context *c)
+              struct snmp_pdu *c)
 {
   ASSUME(oid != NULL);
 
-  snmp_log("critical part");
   if (c->size < snmp_varbind_hdr_size_from_oid(oid))
     snmp_manage_tbuf(p, c);
 
-  snmp_log("critical part done");
-
   struct agentx_varbind *vb = snmp_create_varbind(c->buffer, oid);
 
   if (oid->n_subid < 2 || (oid->prefix != SNMP_MGMT && oid->ids[0] != SNMP_MIB_2))
@@ -1384,7 +1404,7 @@ snmp_mib_fill2(struct snmp_proto *p, struct oid *oid,
  *  are invalidated!
  */
 void
-snmp_manage_tbuf(struct snmp_proto UNUSED *p, struct snmp_pdu_context *c)
+snmp_manage_tbuf(struct snmp_proto UNUSED *p, struct snmp_pdu *c)
 {
   snmp_log("snmp_manage_tbuf()");
   sock *sk = p->sock;
@@ -1403,7 +1423,7 @@ snmp_tx(sock UNUSED *sk)
 
 
 static struct agentx_response *
-prepare_response(struct snmp_proto *p, struct snmp_pdu_context *c)
+prepare_response(struct snmp_proto *p, struct snmp_pdu *c)
 {
   snmp_log("prepare_response()");
 
index 1664df94494232fa473b2bdbb9c70729c2a5f740..537e660456fdb91a2cd6364ae761f1c37c87df8c 100644 (file)
@@ -27,6 +27,8 @@ void snmp_ping(struct snmp_proto *p);
 
 extern u32 snmp_internet[4];
 
+#define SNMP_DEFAULT_CONTEXT 0
+
 enum SNMP_CLASSES {
   SNMP_CLASS_INVALID = 0,
   SNMP_CLASS_BGP = 1,
@@ -64,6 +66,9 @@ enum snmp_search_res {
 
 #define AGENTX_PRIORITY                127
 
+#define SNMP_REGISTER_TREE 0
+#define SNMP_REGISTER_INSTANCE 1
+
 #define SNMP_NATIVE
 
 #ifdef SNMP_NATIVE
@@ -95,7 +100,7 @@ enum snmp_search_res {
 
 #define SNMP_BLANK_HEADER(h, t) SNMP_HEADER(h, t, AGENTX_FLAG_BLANK)
 
-#define SNMP_SESSION(h, p)                                                   \
+#define SNMP_SESSION(h, p)                                                   \
   STORE_U32(h->session_id, p->session_id);                                   \
   STORE_U32(h->transaction_id, p->transaction_id);                           \
   STORE_U32(h->packet_id, p->packet_id)
@@ -104,23 +109,39 @@ enum snmp_search_res {
 #define LOAD_U16(v, bo) ((bo) ? get_u16(&v) : (u16) (v))
 #define LOAD_PTR(v, bo) ((bo) ? get_u32(v) : *((u32 *) v))
 
-#define LOAD_STR(proto, buf, str, length, byte_order) ({                     \
+#define LOAD_STR(/* byte * */buf, str, length, byte_ord)  ({                 \
+  length = LOAD_PTR(buf, byte_ord);                                          \
+  length > 0 ? (str = buf + 4) : (str = NULL); })
+
+#define COPY_STR(proto, buf, str, length, byte_order) ({                     \
   length = LOAD_PTR(buf, byte_order);                                        \
   log(L_INFO "LOAD_STR(), %p %u", proto->p.pool, length + 1);                \
   str = mb_alloc(proto->p.pool, length + 1);                                 \
-  memcpy(str, buf, length);                                                  \
+  memcpy(str, buf+4, length);                                                \
   str[length] = '\0'; /* set term. char */                                   \
-  buf += snmp_str_size_from_len(length); })
+  buf += 4 + snmp_str_size_from_len(length); })
+
+#define SNMP_LOAD_CONTEXT(hdr, buf, cont, cont_len) ({                       \
+  if ((hdr)->flags & AGENTX_NON_DEFAULT_CONTEXT)                             \
+    LOAD_STR((buf), (cont), (cont_len),                                              \
+      (hdr)->flags & AGENTX_NETWORK_BYTE_ORDER); })
 
-#define SNMP_LOAD_CONTEXT(proto, hdr, buf, cont, cont_len)                   \
+#define SNMP_COPY_CONTEXT(proto, hdr, buf, cont, cont_len) ({                \
   cont = NULL; cont_len = 0;                                                 \
   if (hdr->flags & AGENTX_NON_DEFAULT_CONTEXT)                               \
-    LOAD_STR(proto, buf, cont, cont_len,                                     \
-       hdr->flags & AGENTX_NETWORK_BYTE_ORDER)
+    COPY_STR(proto, buf, cont, cont_len,                                     \
+       (hdr)->flags & AGENTX_NETWORK_BYTE_ORDER) })
 
 #define SNMP_HAS_CONTEXT(hdr)                                                \
   hdr->flags |= AGENTX_NON_DEFAULT_CONTEXT
 
+#define SNMP_NON_DEFAULT_CONTEXT(hdr,pdu,contid) ({                          \
+  if (contid) {                                                                      \
+    SNMP_HAS_CONTEXT(hdr);                                                   \
+    snmp_put_str((c).buffer, (sc)->context);                                 \
+    ADVANCE((c).buffer, (c).size, snmp_str_size((sc)->context));             \
+  } })
+
 #define SNMP_PUT_OID(buf, size, oid, byte_ord)                               \
   ({                                                                         \
     struct agentx_varbind *vb = (void *) buf;                                \
@@ -191,8 +212,7 @@ struct agentx_close_pdu {
   u8 reason;
 };
 
-struct agentx_un_register_pdu {
-  struct agentx_header h;
+struct agentx_un_register_hdr {
   u8 timeout;
   u8 priority;
   u8 range_subid;
@@ -280,13 +300,8 @@ enum agentx_response_err {
   AGENTX_RES_PROCESSING_ERR        = 268,
 } PACKED;
 
-struct agentx_context {
-  char *context;  /* string name of this context */
-  uint length;   /* normal strlen() size */
-  /* XXX add buffered context hash? */
-};
-
-struct snmp_pdu_context {
+/* SNMP PDU buffer info */
+struct snmp_pdu {
   byte *buffer;                            /* pointer to buffer */
   uint size;                       /* unused space in buffer */
   uint context;                            /* context hash */
@@ -305,11 +320,11 @@ struct agentx_alloc_context {
 int snmp_rx(sock *sk, uint size);
 int snmp_rx_stop(sock *sk, uint size);
 void snmp_down(struct snmp_proto *p);
-void snmp_register(struct snmp_proto *p, struct oid *oid, uint index, uint len, u8 is_instance);
-void snmp_unregister(struct snmp_proto *p, struct oid *oid, uint index, uint len);
+void snmp_register(struct snmp_proto *p, struct oid *oid, uint index, uint len, u8 is_instance, uint contid);
+void snmp_unregister(struct snmp_proto *p, struct oid *oid, uint index, uint len, uint contid);
 void snmp_notify_pdu(struct snmp_proto *p, struct oid *oid, void *data, uint size, int include_uptime);
 
-void snmp_manage_tbuf(struct snmp_proto *p, struct snmp_pdu_context *c);
+void snmp_manage_tbuf(struct snmp_proto *p, struct snmp_pdu *c);
 
 struct oid *snmp_prefixize(struct snmp_proto *p, const struct oid *o, int byte_ord);
 u8 snmp_get_mib_class(const struct oid *oid);