]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
TMP new snmp PDUs
authorVojtech Vilimek <vojtech.vilimek@nic.cz>
Tue, 6 Sep 2022 16:04:29 +0000 (18:04 +0200)
committerVojtech Vilimek <vojtech.vilimek@nic.cz>
Tue, 6 Sep 2022 16:04:29 +0000 (18:04 +0200)
proto/snmp/Makefile
proto/snmp/bgp_mib.h
proto/snmp/config.Y
proto/snmp/snmp.c
proto/snmp/snmp.h
proto/snmp/subagent.c
proto/snmp/subagent.h

index ca589059c45a9e6c699cb0a55df36d7e6b7e550c..b80cb135a4a82482c5b2bf2dc907371638bc6e3d 100644 (file)
@@ -1,6 +1,5 @@
 src := snmp.c subagent.c
 obj := $(src-o-files)
-LIBS += `net-snmp-config --agent-libs`
 $(all-daemon)
 $(cf-local)
 $(call proto-build,snmp_build)
index 99229521c0252dafa6054768f60cd12006cacb63..dd34fa18676f6490f5b6c637650b118552536190 100644 (file)
@@ -2,30 +2,32 @@
 #define _BIRD_SNMP_BGP_MIB_H_
 
 /* peers attributes */
-#define SNMP_BGP_IDENTIFIER 1
-#define SNMP_BGP_STATE 2
-#define SNMP_BGP_ADMIN_STATUS 3                    /* in read-only mode */
-#define SNMP_BGP_VERSION 4
-#define SNMP_BGP_LOCAL_ADDR 5
-#define SNMP_BGP_LOCAL_PORT 6
-#define SNMP_BGP_REMOTE_ADDR 7
-#define SNMP_BGP_REMOTE_PORT 8
-#define SNMP_BGP_REMOTE_AS 9
-#define SNMP_BGP_RX_UPDATES 10             /* in updates */
-#define SNMP_BGP_TX_UPDATES 11             /* out updates */
-#define SNMP_BGP_RX_MESSAGES 12                    /* in total messages */
-#define SNMP_BGP_TX_MESSAGES 13                    /* out total messages */
-#define SNMP_BGP_LAST_ERROR 14             /* UNSUPPORTED */
-#define SNMP_BGP_FSM_TRANSITIONS 15        /* FSM established transitions */
-#define SNMP_BGP_FSM_ESTABLISHED_TIME 16    /* UNSUPPORTED FSM established time */
-#define SNMP_BGP_RETRY_INTERVAL 17
-#define SNMP_BGP_HOLD_TIME 18
-#define SNMP_BGP_KEEPALIVE 19
-#define SNMP_BGP_HOLD_TIME_CONFIGURED 20
-#define SNMP_BGP_KEEPALIVE_CONFIGURED 21
-#define SNMP_BGP_ORIGINATION_INTERVAL 22    /* UNSUPPORTED */
-#define SNMP_BGP_MIN_ROUTE_ADVERTISEMENT 23 /* UNSUPPORTED */
-#define SNMP_BGP_MIN_UPDATE_ELAPSED_TIME 24 /* UNSUPPORTED */
+enum BGP4_MIB {
+  SNMP_BGP_IDENTIFIER              =  1;
+  SNMP_BGP_STATE                   =  2;
+  SNMP_BGP_ADMIN_STATUS                    =  3;   /* in read-only mode */
+  SNMP_BGP_VERSION                 =  4;
+  SNMP_BGP_LOCAL_ADDR              =  5;
+  SNMP_BGP_LOCAL_PORT              =  6;
+  SNMP_BGP_REMOTE_ADDR             =  7;
+  SNMP_BGP_REMOTE_PORT             =  8;
+  SNMP_BGP_REMOTE_AS               =  9;
+  SNMP_BGP_RX_UPDATES              = 10;   /* in updates */
+  SNMP_BGP_TX_UPDATES              = 11;   /* out updates */
+  SNMP_BGP_RX_MESSAGES             = 12;   /* in total messages */
+  SNMP_BGP_TX_MESSAGES             = 13;   /* out total messages */
+  SNMP_BGP_LAST_ERROR              = 14;
+  SNMP_BGP_FSM_TRANSITIONS         = 15;   /* FSM established transitions */
+  SNMP_BGP_FSM_ESTABLISHED_TIME            = 16;   /* UNSUPPORTED FSM established time */
+  SNMP_BGP_RETRY_INTERVAL          = 17;
+  SNMP_BGP_HOLD_TIME               = 18;
+  SNMP_BGP_KEEPALIVE               = 19;
+  SNMP_BGP_HOLD_TIME_CONFIGURED            = 20;
+  SNMP_BGP_KEEPALIVE_CONFIGURED            = 21;
+  SNMP_BGP_ORIGINATION_INTERVAL            = 22;   /* UNSUPPORTED 0 */
+  SNMP_BGP_MIN_ROUTE_ADVERTISEMENT  = 23;   /* UNSUPPORTED 0*/
+  SNMP_BGP_MIN_UPDATE_ELAPSED_TIME  = 24;   /* UNSUPPORTED */
+} PACKED;
 
 void snmp_init_bgp_table(void);
 void snmp_del_bgp_table(void);
index 7593ef5f8bdf57d508ea2cd06fd5f519d1a11059..d85dbf15c6ab5d419313107cc4d128d094c8198f 100644 (file)
@@ -61,7 +61,8 @@ snmp_bgp_bond: BGP symbol
 
   struct proto_config *pc;
   WALK_LIST(pc, this_proto->global->protos)
-    if (!strcmp(pc->name, $2->name) && pc->protocol == &proto_bgp)
+    if (!strcmp(pc->name, $2->name) && pc->protocol == &proto_bgp
+        && !ipa_zero(((struct bgp_proto *) pc)->remote_ip)) 
       this_bond->proto = pc;
 
   if (!this_bond->proto) cf_error("BGP protocol %s not found", $2->name);
index 3f76a4c4607f0208d0c029d364618da5ef3a78b4..c00dd94febe21b8e4c450376017ed6d3f4689f73 100644 (file)
@@ -35,6 +35,7 @@ snmp_init(struct proto_config *CF)
   p->remote_ip = cf->remote_ip;
   p->local_port = cf->local_port;
   p->remote_port = cf->remote_port;
+  p->state = SNMP_INIT;
 
   // p->timeout = cf->timeout;
   p->timeout = 15;
@@ -59,7 +60,7 @@ snmp_start_locked(struct object_lock *lock)
   s->rbsize = SNMP_RX_BUFFER_SIZE;
   s->tbsize = SNMP_TX_BUFFER_SIZE;
   
-  // s->tos = IP_PREC_INTERNET_CONTROL
+  //s->tos = IP_PREC_INTERNET_CONTROL
   //s->rx_hook = snmp_connected;
   s->tx_hook = snmp_connected;
   s->err_hook = snmp_sock_err;
@@ -76,7 +77,7 @@ snmp_start_locked(struct object_lock *lock)
 static void
 snmp_tx(sock *sk UNUSED)
 {
-  log(L_INFO "snmp_tx() recieved something, yay!");
+  log(L_INFO "snmp_tx() something, yay!");
 }
 
 
@@ -96,7 +97,8 @@ snmp_connected(sock *sk)
 static void
 snmp_sock_err(sock *sk UNUSED, int err UNUSED)
 {
-  log(L_INFO "snmp_sock_err() ");
+  log(L_INFO "snmp_sock_err() %s - err no: %d",  strerror(err), err);
+  die("socket error");
 }
 
 static int
@@ -104,9 +106,10 @@ snmp_start(struct proto *P)
 {
   log(L_INFO "snmp_start() - starting timer (almost)");
   struct snmp_proto *p = (void *) P;
+  struct snmp_config *cf = P->cf;
 
   p->ping_timer = tm_new_init(p->p.pool, snmp_ping_timer, p, 0, 0);
-  tm_set(p->ping_timer, current_time() + (7 S_)); 
+  tm_set(p->ping_timer, current_time() + (2 S_));
 
   /* starting agentX communicaiton channel */
   log(L_INFO "preparing lock");
@@ -122,7 +125,30 @@ snmp_start(struct proto *P)
 
   log(L_INFO "local ip: %I:%u, remote ip: %I:%u",
     p->local_ip, p->local_port, p->remote_ip, p->remote_port);
-  
+
+  init_list(p->bgp_entries);
+
+  /* create copy of bonds to bgp */
+  HASH_INIT(p->peer_hash, p->p.pool, 10);
+
+  struct snmp_bond *b;
+  WALK_LIST(b, cf->bgp_entries)
+  {
+    struct bgp_config *bc = b->proto;
+    if (bc && !ipa_zero(bc->remote_ip))
+    {
+      struct snmp_bgp_peer_entry pe = {
+       .bond = b;
+       .peer_ip = bc->remote_ip;
+       .next = NULL;
+      }; 
+
+      HASH_INSERT(p->peer_hash, SNMP_HASH, pe)
+       
+      add_tail(&p->bgp_entries, b);
+    }
+  }
+   
   return PS_START; 
 }
 
@@ -138,13 +164,13 @@ snmp_reconfigure(struct proto *P, struct proto_config *CF)
   p->remote_port = cf->remote_port;
   p->timeout = 15;
 
+  /* TODO walk all bind protocols and find their (new) IP
+    to update HASH table */
   log(L_INFO "snmp_reconfigure() lip: %I:%u rip: %I:%u",
     p->local_ip, p->local_port, p->remote_ip, p->remote_port);
-  return 0;
+  return PS_START;
 }
-
-static void
-snmp_show_proto_info(struct proto *P)
+static void snmp_show_proto_info(struct proto *P)
 {
   //struct snmp_proto *sp = (void *) P;
   struct snmp_config *c = (void *) P->cf;
@@ -217,16 +243,17 @@ snmp_ping_timer(struct timer *tm)
   log(L_INFO "snmp_ping_timer() ");
   struct snmp_proto *p = tm->data;  
 
-  // ping here
-  ping_pdu(p); 
+  snmp_ping(p);
+
+  //tm_set(tm, current_time() + (7 S_));
 }
 
-/* snmp_shutdown already occupied by net-snmp */
 static int
 snmp_shutdown(struct proto *P)
 {
   struct snmp_proto *p = SKIP_BACK(struct snmp_proto, p, P);
   snmp_stop_subagent(p);
+  return PS_DOWN;
 }
 
 struct protocol proto_snmp = {
@@ -242,7 +269,6 @@ struct protocol proto_snmp = {
   .show_proto_info =   snmp_show_proto_info,
 };
 
-/* strange name because of conflict with net-snmp lib snmp_lib() */
 void
 snmp_build(void)
 {
index 90236c5cc6dc33663fcfe9515d0e77376720d6bd..bb38002cdadd03d12a1111da4b51ff32eef17496 100644 (file)
 #define SNMP_RX_BUFFER_SIZE 2048
 #define SNMP_TX_BUFFER_SIZE 2048
 
+#define SNMP_INIT 1
+#define SNMP_REGISTR 2
+#define SNMP_CONN 3
+#define SNMP_ERR 4
+
+#define SNMP_OFF 0
+
+
+/* hash table macros */
+#define SNMP_HASH_KEY(n)  n->peer_ip;
+#define SNMP_HASH_NEXT(n) n->next;
+#define SNMP_HASH_EQ(ip1, ip2) ipa_equal(ip1, ip2)
+#define SNMP_HASH_FN(ip)  ipa_hash(ip)
+
 struct snmp_bond {
   node n;
   struct proto_config *proto;
@@ -45,24 +59,40 @@ struct snmp_config {
   list bgp_entries;
 };
 
+struct snmp_bgp_peer_entry {
+  struct snmp_bond *bond;
+  ip_addr peer_ip;
+  struct snmp_bgp_peer_entry *next;
+}
+
 struct snmp_proto {
   struct proto p;
   struct object_lock *lock;
   ip_addr local_ip;
   ip_addr remote_ip;
   u16 local_port;
   u16 remote_port;
+
   sock *sock;
   u8 timeout;
+
   u32 session_id;
   u32 transaction_id;
   u32 packet_id;
+
   //struct iface *iface;
   // map goes here
+  HASH(struct snmp_bgp_peer_entry) bgp_hash;
   struct tbf rl_gen;
+
   timer *ping_timer;
+  u8 state;
+
+  list bgp_entries;
 };
 
+/*
 struct snmp_channel_config {
   struct channel_config c;
   struct bgp_config *bgp;
@@ -72,7 +102,7 @@ struct snmp_channel_config {
 struct snmp_channel {
   struct channel c;
 };
-
+*/
 
 
 #endif
index f7aa4c39b9c4f19a885402156bef2d742f0761c1..02638848bd7224a7d9a98476c28f91982de8943c 100644 (file)
 #include "lib/unaligned.h"
 #include "subagent.h"
 
+/* =============================================================
+ *  Problems
+ *  ------------------------------------------------------------
+ *
+ *    change of remote ip -> no notification, no update 
+ *    same ip, different ports
+ *    distinct VRF (two interfaces with overlapping private addrs)
+ *
+ */
+
 static int parse_response(struct snmp_proto *p, byte *buf, uint size);
 static void header_update_len(byte *buf, u32 len);
-static uint oid_size(struct oid* o);
+static inline uint oid_size(struct oid *o);
+static inline uint vb_size(struct agentx_varbind *vb);
+static int snmp_stop_ack(sock *sk, uint size);
+static void do_response(struct snmp_proto *p, byte *buf, uint size);
+static int parse_get_pdu(struct snmp_proto *p, byte *buf, uint size);
+static void response_fail(struct snmp_proto *p, u16 err_no, u16 index);
+static byte *prepare_response(struct snmp_proto *p, byte *buf, uint size, u16 err_no, u16 index);
 
 static const char * const snmp_errs[] = {
   #define SNMP_ERR_SHIFT 256
@@ -32,123 +48,241 @@ static const char * const snmp_errs[] = {
   [AGENTX_RES_PROCESSING_ERR - SNMP_ERR_SHIFT] = "Processing error",
 };
 
-static int
-put_str(byte *buf, const char *str, uint *size)
+/* payload length in bytes */
+static inline size_t
+pkt_len(byte *buf, byte *pkt)
+{
+  return (pkt - buf) - AGENTX_HEADER_SIZE;
+}
+
+static inline size_t
+str_size(const char *str)
+{
+  return 4 + BIRD_ALIGN(strlen(str), 4);
+}
+
+static byte *
+put_str(byte *buf, const char *str)
 {
   uint len = strlen(str);
-  uint pkt_len = BIRD_ALIGN(len, 4);
+  uint slen = BIRD_ALIGN(len, 4);
 
   if (len > MAX_STR)
-    return -1;
+    return NULL;
+
+  STORE_PTR(buf, len);
 
-  put_u32(buf, len);
   memcpy(buf + 4, str, len);
 
-  // make the value 32-bit aligned
-  for (uint i = 0; i < pkt_len - len; i++)
+  for (uint i = 0; i < slen - len; i++)
     buf[len + i] = 0x00;  // PADDING
 
-  *size += (4 + pkt_len);
-
-  return 0;
+  return buf + str_size(str);
 }
 
-static void
-put_blank(byte *buf, uint *size)
+static byte *
+put_blank(byte *buf)
 {
-  buf[0] = buf[1] = buf[2] = buf[3] = 0; 
-  *size += 4;
+  STORE_PTR(buf, 0);
+  return buf + 4;
 }
 
-static void
-put_oid(byte *buf, struct oid *oid, uint *size)
+static byte *
+put_oid(byte *buf, struct oid *oid)
 {
+  log(L_INFO "testing oid");
+  for (uint i = 0; i < oid->n_subid; i++)
+    log(L_INFO "oid id %d:  %u", i, oid->ids[i]);
+  log(L_INFO "put_oid()");
   put_u8(buf, oid->n_subid);
-  put_u8(buf + 1, oid->prefix);
-  put_u8(buf + 2, oid->include);
-  put_u8(buf + 3, 0);  // PADDING
-
-  put_u32s(buf + 4, oid->subid.ids, oid->subid.len); 
+  log(L_INFO "data %p: %02X", buf, *buf);
+  put_u8(++buf, oid->prefix);
+  log(L_INFO "data %p: %02X", buf, *buf);
+  put_u8(++buf, oid->include);
+  log(L_INFO "data %p: %02X", buf, *buf);
+  put_u8(++buf, 0);  // PADDING
+  /* last increment */
+  ++buf;
+  log(L_INFO "oid head end %p", buf);
+
+  /* copy OID data */
+#ifdef SNMP_NATIVE
+  for (uint i = 0; i < oid->n_subid; i++)
+    *(((u32 *) buf) + i) = oid->ids[i];
+#else
+  put_u32s(buf, oid->ids, oid->n_subid << 2);
+#endif
+/*
+  for (uint i = 0; i <= (oid->n_subid << 2) +4 ; i += 4)
+    log(L_INFO "OID % 3u: %02X %02X %02X %02X", i,
+      *(buf - 4 + i),
+      *(buf - 4 + i + 1),
+      *(buf - 4 + i + 2),
+      *(buf - 4  + i + 3)
+  );
+*/
 
-  *size += (4 + oid->subid.len);
+  return buf + (oid->n_subid << 2);
 }
 
+
 /* paste data at first byte in message 
  *   with 3B of padding
  */
-static void
-paste_fbyte(byte *buf, u8 data, uint *size)
+static byte *
+paste_fbyte(byte *buf, u8 data)
 {
-  buf[0] = data;
-  buf[1] = buf[2] = buf[3] = 0x00; // PADDING  
-  *size += 4;
+  log(L_INFO "paste_fbyte()");
+  put_u8(buf, data);
+  put_u24(++buf, 0);  // PADDING
+  return buf + 3;
 }
 
-static u32
-store_in_order(u32 val, int order)
+static void
+open_pdu(struct snmp_proto *p, struct oid *oid)
 {
-  /* AGENTX_BIG_ENDIAN */
-  if (order)
+  sock *sk = p->sock;
+  byte *buf, *pkt;
+  buf = pkt = sk->tbuf;
+  uint size = sk->tbsize;
+
+  // should be configurable
+  const char *str = "bird";
+
+  //uint pkt_size = 0;
+
+  if (size > AGENTX_HEADER_SIZE + oid_size(oid) + str_size(str))
   {
+    log(L_INFO "open_pdu()");
+
+    struct agentx_header *h;
+    SNMP_CREATE(pkt, struct agentx_header, h)
+    SNMP_B_HEADER(h, AGENTX_OPEN_PDU)
+
+    STORE(h->session_id, 1);
+    STORE(h->transaction_id, 1);
+    STORE(h->packet_id, 1);
+
+    pkt = paste_fbyte(pkt, p->timeout);
+    pkt = put_oid(pkt, oid);
+    pkt = put_str(pkt, str);
+
+    SNMP_UPDATE(h, pkt_len(buf, pkt));
+
+    int ret = sk_send(sk, pkt - buf);
+
+    if (ret == 0)
+      log(L_INFO "sleep");
+    else if (ret < 0)
+      log(L_INFO "err %d", ret);
+    else
+      log(L_INFO "ok !!! ");
   }
+
   else
-  {
-  } 
-  return 0;
+    log(L_INFO "open_pdu() insufficient size, %u <= %u ",
+       size, AGENTX_HEADER_SIZE + oid_size(oid) + str_size(str));
 }
 
+/* index allocate / deallocate pdu * /
 static void
-open_pdu(struct snmp_proto *p, struct oid *oid)
+de_allocate_pdu(struct snmp_proto *p, struct oid *oid, u8 type)
 {
   sock *sk = p->sock;
-  byte *buf, *pkt, *end;
+  byte *buf, *pkt;
   buf = pkt = sk->tbuf;
   uint size = sk->tbsize;
 
-  // should be configurable
-  const char *str = "bird";
+  if (size > AGENTX_HEADER_SIZE + )
+  {
+    log(L_INFO "de_allocate_pdu()");
 
-  uint pkt_size = 0;
-  uint slen = BIRD_ALIGN(strlen(str), 4);
+    struct agentx_header *h;
+    SNMP_CREATE(pkt, struct agentx_header, h);
+    SNMP_B_HEADER(h, type);
+    SNMP_SESSION(h,p);
 
-  /* +8 - header of oid (4) and octet string length (4) */
-  if (size > AGENTX_HEADER_SIZE + oid->subid.len + slen + 8)
+    struct agentx_varbind *vb = (struct agentx_varbind *) pkt;
+    STORE_16(vb->type, AGENTX_OBJECT_ID);
+    STORE(vb->oid,
+  }
+
+  else
+    log(L_INFO "de_allocate_pdu(): insufficient size");
+}
+*/
+
+/* register / unregister pdu */
+static void
+un_register_pdu(struct snmp_proto *p, struct oid *oid, uint index, uint len, u8 type)
+{
+  sock *sk = p->sock;
+  byte *buf, *pkt;
+  buf = pkt = sk->tbuf;
+  uint size = sk->tbsize;
+
+  /* conditional +4 for upper-bound */
+  if (size > AGENTX_HEADER_SIZE + oid_size(oid) + ((len > 1) ? 4 : 0))
   {
-    log(L_INFO "open_pdu() sufficient size nw order: %u",
-AGENTX_NETWORK_BYTE_ORDER);
-    PASTE_HEADER(pkt, AGENTX_OPEN_PDU, AGENTX_NETWORK_BYTE_ORDER, size);
+    log(L_INFO "un_register_pdu()");
+    struct agentx_un_register_pdu *ur;
+    SNMP_CREATE(pkt, struct agentx_un_register_pdu, ur);
+    struct agentx_header *h = &ur->h;
 
-    // use random num instead
-    put_u32(&h->session_id, 1);
-    put_u32(&h->transaction_id, 1);
-    put_u32(&h->packet_id, 1);
+    // FIXME correctly set INSTANCE REGISTRATION bit
+    SNMP_HEADER(h, type, AGENTX_FLAG_INSTANCE_REGISTRATION);
+    SNMP_SESSION(h, p);
 
-    paste_fbyte(pkt, p->timeout, &pkt_size);
-    ADVANCE(pkt, size, 4);
-  
-    put_oid(pkt, oid, &pkt_size);
-    ADVANCE(pkt, size, oid_size(oid)); 
+    /* do not override timeout */
+    STORE(ur->timeout, 0);
+    /* default priority */
+    STORE(ur->priority, AGENTX_PRIORITY);
+    STORE(ur->range_subid, (len > 1) ? index : 0);
 
-    /* paste description */
-    put_str(pkt, str, &pkt_size);
-    ADVANCE(pkt, size, slen);
+    pkt = put_oid(pkt, oid);
+    log(L_INFO "pkt - buf : %lu sizeof %u", pkt -buf, AGENTX_HEADER_SIZE);
+
+    /* place upper-bound if needed */
+    if (len > 1)
+    {
+      STORE_PTR(pkt, len);
+      pkt += 4;
+    }
+
+    log("size of pkt: %u", pkt_len(buf,pkt));
+    SNMP_UPDATE(h, pkt_len(buf, pkt));
+
+    for (uint i = 0; i < pkt - buf; i++)
+      log(L_INFO "%p:  %02X", buf+i, *(buf + i));
+
+    log(L_INFO "sending (un)register %d", type);
+    int ret = sk_send(sk, pkt - buf);
 
-    header_update_len(buf, pkt_size);
-   
-    log(L_INFO "sk_send()-ing %u", AGENTX_HEADER_SIZE + pkt_size); 
-    int ret = sk_send(sk, AGENTX_HEADER_SIZE + pkt_size);
     if (ret == 0)
       log(L_INFO "sleep");
     else if (ret < 0)
       log(L_INFO "err %d", ret);
     else
-      log(L_INFO "ok !!");
+      log(L_INFO "ok !!");
   }
 
   else
-    log(L_INFO "open_pdu() insufficient size, %u <= %u ",
-       size, AGENTX_HEADER_SIZE + oid->subid.len + slen + 8);
+    log(L_INFO "un_register_pdu() insufficient size");
+}
+
+/* register pdu */
+static void
+snmp_register(struct snmp_proto *p, struct oid *oid, uint index, uint len)
+{
+  un_register_pdu(p, oid, index, len, AGENTX_REGISTER_PDU);
+}
+
+
+/* unregister pdu */
+static void
+snmp_unregister(struct snmp_proto *p, struct oid *oid, uint index, uint len)
+{
+  un_register_pdu(p, oid, index, len, AGENTX_UNREGISTER_PDU);
 }
 
 static void
@@ -164,34 +298,36 @@ close_pdu(struct snmp_proto *p, u8 reason)
   /* +4B for reason */
   if (size > AGENTX_HEADER_SIZE + 4)
   {
-    PASTE_HEADER(buf, AGENTX_CLOSE_PDU, AGENTX_NETWORK_BYTE_ORDER, size);
+    struct agentx_header *h;
+    SNMP_CREATE(pkt, struct agentx_header, h)
+    SNMP_B_HEADER(h, AGENTX_CLOSE_PDU)
 
-    log(L_INFO "session_id %u", p->session_id);
-    h->session_id = p->session_id;
-    p->transaction_id++;
-    p->transaction_id = get_u32(&p->transaction_id); 
-    put_u32(&h->transaction_id, p->transaction_id);
-    put_u32(&h->packet_id, 1);
+    SNMP_SESSION(h, p)
 
-    ADVANCE(pkt, size, sizeof(struct agentx_header));
-    
-    paste_fbyte(pkt, reason, &size);
-    ADVANCE(pkt, size, 4);
+    pkt = paste_fbyte(pkt, reason);
 
-    /* 4 - reason size */
-    header_update_len(sk->tbuf, 4);
+    SNMP_UPDATE(h, pkt_len(buf, pkt));
 
-    int ret = sk_send(sk, sizeof(struct agentx_header) + 4);
+    log(L_INFO "preparing to sk_send()");
+    int ret = sk_send(sk, pkt - buf);
 
     if (ret == 0)
       log(L_INFO "sleep");
     else if (ret < 0)
       log(L_INFO "err");
     else 
-      log(L_INFO, "ok !! ");
+      log(L_INFO, "ok !!");
   }
 }
 
+static inline void
+refresh_ids(struct snmp_proto *p, struct agentx_header *h)
+{
+  int byte_ord = h->flags & AGENTX_NETWORK_BYTE_ORDER;
+  p->transaction_id = LOAD(h->transaction_id, byte_ord);
+  p->packet_id = LOAD(h->packet_id, byte_ord);
+}
+
 static int
 parse_pkt(struct snmp_proto *p, byte *buf, uint size)
 {
@@ -199,15 +335,19 @@ parse_pkt(struct snmp_proto *p, byte *buf, uint size)
     return 0;
 
   struct agentx_header *h = (void *) buf;
+  log(L_INFO "parse_pkt got type %u", h->type);
   switch (h->type)
   {
     case AGENTX_RESPONSE_PDU:
       return parse_response(p, buf, size);
-      break;
+
+    case AGENTX_GET_PDU:
+      refresh_ids(p, h);
+      return parse_get_pdu(p, buf, size);
 
     /* should not happen */
     default:
-      die("unknown packet type");
+      die("unknown packet type %u", h->type);
   }
 }
 
@@ -220,24 +360,165 @@ parse_response(struct snmp_proto *p, byte *buf, uint size)
   struct agentx_response *r = (void *) buf;
   struct agentx_header *h = &r->h;
 
-  log(L_INFO "endianity: %s, session %u", (h->flags & AGENTX_NETWORK_BYTE_ORDER) ? "big end":
-"little end", h->session_id);
-  p->session_id = h->session_id;
-  p->transaction_id = h->transaction_id;
-  p->packet_id = h->packet_id;
+  log(L_INFO "endianity: %s, session %u, transaction: %u", (h->flags & AGENTX_NETWORK_BYTE_ORDER) ? "big end":
+"little end", h->session_id, h->transaction_id);
+  log(L_INFO "sid: %3u\ttid: %3u\tpid: %3u\t", p->session_id, p->transaction_id,
+p->packet_id);
+
+  log(L_INFO "size %u", h->payload);
+  log(L_INFO "uptime: %u s", r->uptime);
+
+  if (r->err == AGENTX_RES_NO_ERROR)
+    do_response(p, buf, size);
+  else
+    log(L_INFO "an error occured '%s'", snmp_errs[get_u16(&r->err) -
+SNMP_ERR_SHIFT]);
+
+  return 1;
+}
+
+static void
+do_response(struct snmp_proto *p, byte *buf, uint size)
+{
+  struct agentx_response *r = (void *) buf;
+  struct agentx_header *h = &r->h;
 
-  log(L_INFO "size %u", get_u32(&h->payload));
-  log(L_INFO "uptime: %u s", get_u32(&r->uptime)); 
-  switch (r->err)
+  /* TODO make it asynchronous for better speed */
+  switch (p->state)
   {
-    case AGENTX_RES_NO_ERROR:
+    case SNMP_INIT:
+      if (h->flags & AGENTX_NETWORK_BYTE_ORDER)
+      {
+       p->session_id = get_u32(&h->session_id);
+       p->transaction_id = get_u32(&h->transaction_id);
+       p->packet_id = get_u32(&h->packet_id);
+      }
+      else
+      {
+       memcpy(&p->session_id, &h->session_id, 12);
+      }
+
+      p->transaction_id++;
+
+      log(L_INFO "sending register-pdu");
+      u32 arr_with_prefix[] = {1, 15, 3, 1, 1};
+      struct oid *o2 = mb_allocz(p->p.pool, 10 * 4);
+      put_u8(&o2->n_subid, 9);
+      put_u8(&o2->prefix, 2);
+
+      memcpy(o2->ids, arr_with_prefix, 5 * 4);
+      u32 remote_addr[] = {10, 0, 0, 0};
+      memcpy(o2->ids + 5, remote_addr, 4 * 4);
+
+      // register first line in BGP4-MIB bgpPeerTable
+      // TODO register all bind bgp connections
+      snmp_register(p, o2, 9, 24);
+
+      // register whole BGP4 mib-tree section
+      u32 arr_bgp[] = {1, 15, 1};
+      struct oid *o3 = mb_allocz(p->p.pool, 4 * 4);
+      put_u8(&o3->n_subid, 3);
+      put_u8(&o3->prefix, 2);
+     
+      memcpy(o3->ids, arr_bgp, 3 * 4); 
+
+      snmp_register(p, o3, 0, 1);
+
+      p->state = SNMP_REGISTR;
+      //proto_notify_state(&p->p, PS_UP);
       break;
-    default:
-      log(L_INFO "an error occured: '%s'", snmp_errs[get_u16(&r->err) -
-SNMP_ERR_SHIFT]);
+
+    case SNMP_REGISTR:
       break;
+
+    case SNMP_CONN:
+      break;
+
+    default:
+      die("unkonwn SNMP state");
   }
-  proto_notify_state(&p->p, PS_UP);
+}
+
+static int
+parse_get_pdu(struct snmp_proto *p, byte *buf, uint size)
+{
+  log(L_INFO "parse_get_pdu()");
+
+  sock *sk = p->sock;
+  byte *res_pkt, *res = sk->tbuf;
+  uint rsize = sk->tbsize;
+
+  if (size < AGENTX_HEADER_SIZE)
+    return 0;
+
+  log(L_INFO "Get-PDU enough room %p", buf);
+  struct agentx_header *h = (void *) buf;
+  int byte_ord = h->flags & AGENTX_NETWORK_BYTE_ORDER;
+  ADVANCE(buf, size, AGENTX_HEADER_SIZE);
+  log(L_INFO "advancing %p cause %u", buf, AGENTX_HEADER_SIZE);
+  byte *pkt = buf;
+  uint pkt_size = LOAD(h->payload, byte_ord);
+  log(L_INFO "packet size is %u", pkt_size);
+
+  uint clen;
+  char *context = NULL;
+  SNMP_LOAD_CONTEXT(p, h, pkt, context, clen)
+  log(L_INFO "after context load %p, pkt == buf %d", pkt, pkt == buf);
+
+  /* parsing one search range */
+  struct oid *o_start, *o_end;
+  o_start = (struct oid *) pkt;
+  pkt += oid_size(o_start);
+  o_end = (struct oid *) pkt;  // for Get-PDU always null
+  pkt += oid_size(o_end);
+
+  log(L_INFO "sizes o_start %lu o_end %lu", oid_size(o_start),
+      oid_size(o_end));
+
+  log(L_INFO " creating response header rpkt %p %u", res_pkt, res_pkt - res);
+  log(L_INFO "o_subid: %u o_prefix %u o_include %u ---",
+      o_start->n_subid, o_start->prefix, o_start->include);
+  // TODO read a oid
+  // TODO allow multiple values
+
+  res_pkt = prepare_response(p, res, rsize, AGENTX_RES_NO_ERROR, 0);
+  log(L_INFO "response header created: %p (%u)", res_pkt, res_pkt - res);
+
+  res_pkt = request(res_pkt, o_start);
+  struct agentx_varbind *vb_start;
+  vb_start = (void *) res_pkt;
+  log(L_INFO " SNMP_CREATEish rpkt %p %u", res_pkt, res_pkt - res);
+
+  memcpy(&vb_start->name, o_start, oid_size(o_start)); 
+  STORE_16(vb_start->type, AGENTX_INTEGER);
+  STORE_16(vb_start->pad, 0);  // padding zeroing
+  res_pkt += vb_size(vb_start);
+
+  log(L_INFO " vb_size() rpkt %p %u", res_pkt, res_pkt - res);
+  
+  STORE_PTR(res_pkt, 0x1234ABCD);
+  
+  log(L_INFO " STORE_PTR int-value rpkt %p %u", res_pkt, res_pkt - res);
+  res_pkt += 4; 
+  log(L_INFO " shift rpkt %p %u", res_pkt, res_pkt - res);
+
+  log(L_INFO "after integer write");
+
+  struct agentx_header *rh = (void *) res;
+  SNMP_UPDATE(rh, pkt_len(res, res_pkt));
+  log(L_INFO "res->payload %u (loaded) %u, trying to send: %u",
+    rh->payload, LOAD(rh->payload, rh->flags & AGENTX_NETWORK_BYTE_ORDER),
+    res_pkt - res); 
+   
+  int ret = sk_send(sk, res_pkt - res);
+  log(L_INFO "message sent");
+
+  if (res == 0)
+    log(L_INFO "sleep");
+  else if (ret < 0)
+    log(L_INFO "err no: %d", ret);
+  else
+    log(L_INFO "OK !!");
 
   return 1;
 }
@@ -262,11 +543,13 @@ snmp_start_subagent(struct snmp_proto *p)
   struct oid *o = mb_allocz(p->p.pool, sizeof(struct oid));
   open_pdu(p, o);
   mb_free(o);
+
 }
 
 void
 snmp_stop_subagent(struct snmp_proto *p)
 {
+  log(L_INFO "snmp_stop_subagent()");
   sock *sk = p->sock;
 
   close_pdu(p, AGENTX_CLOSE_SHUTDOWN);
@@ -274,10 +557,18 @@ snmp_stop_subagent(struct snmp_proto *p)
   sk->rx_hook = snmp_stop_ack;
 }
 
-static uint
+static inline uint
 oid_size(struct oid *o)
 {
-  return 4 + o->subid.len;
+  /* faster multipication by 4 */
+  return 4 + (o->n_subid << 2);
+}
+
+static inline uint
+vb_size(struct agentx_varbind *vb)
+{
+  /* +4B for type and pad */
+  return oid_size(&vb->name) + 4;
 }
 
 int
@@ -286,132 +577,337 @@ snmp_rx(sock *sk, uint size)
   log(L_INFO "snmp_rx()");
   struct snmp_proto *p = sk->data;
   byte *pkt = sk->rbuf;
-  byte *end = pkt + size;
 
-  parse_pkt(p, pkt, size);
+  // 1 means all done; 0 means to be continued
+  return parse_pkt(p, pkt, size);
   /* 
-  while (end >= ptk + AGENTX_HEADER_SIZE)
+  while (end >= pkt + AGENTX_HEADER_SIZE)
   {
     parse_header(p);
     parse_pkt(p, );
   }
   */ 
-  return 0;
-  // 1 means all done 
 }
 
-void 
-ping_pdu(struct snmp_proto *p)
+/* ping pdu */
+void
+snmp_ping(struct snmp_proto *p)
 {
   /* this does not support non-default context */ 
   sock *sk = p->sock;
-  byte *buf = sk->tbuf;
+  byte *pkt = sk->tbuf;
   uint size = sk->tbsize;
 
-  PASTE_HEADER(buf, AGENTX_PING_PDU, AGENTX_NETWORK_BYTE_ORDER, size);
-
-  put_u32(&h->session_id, p->session_id);
-  p->transaction_id++;
-  put_u32(&h->transaction_id, p->transaction_id);
-  put_u32(&h->packet_id, 1);
-  put_u32(&h->payload, 0);
+  if (size > AGENTX_HEADER_SIZE)
+  {
+    log(L_INFO "ping_pdu()");
+    struct agentx_header *h;
+    log("before dead %p", pkt );
+    SNMP_CREATE(pkt, struct agentx_header, h);
+    SNMP_B_HEADER(h, AGENTX_PING_PDU);
+    SNMP_SESSION(h, p);
+
+    /* sending only header => pkt - buf */
+    int ret = sk_send(sk, AGENTX_HEADER_SIZE);
+    if (ret == 0)
+      log(L_INFO "sleep");
+    else if (ret < 0)
+      log(L_INFO "err %d", ret);
+    else
+      log("ok ! !");
+  }
 
-  sk_send(sk, AGENTX_HEADER_SIZE);
+  else
+    log(L_INFO "ping_pdu() insufficient size");
 }
 
-/* 
- * cont is optional context 
- * upp_b is upper_bond
- */
-int
-snmp_register_oid(sock *sk, struct oid *subtree, u8 range, const char *cont, u32 upp_b)
+
+static int
+snmp_stop_ack(sock *sk, uint size)
 {
   struct snmp_proto *p = sk->data;
-  byte *buf = sk->tbuf;
-  uint size = sk->tbsize; 
-  log(L_INFO "snmp_register_oid() ");
-
-  u8 flags = AGENTX_NETWORK_BYTE_ORDER | ((cont) ? AGENTX_NON_DEFAULT_CONTEXT :
-0);
-  PASTE_HEADER(buf, AGENTX_REGISTER_PDU, flags, size);
-
-  if (cont)
-    put_str(buf, cont, &size);
-
-  put_u8(buf, p->timeout);
-  put_u8(buf + 1, AGENTX_PRIORITY);
-  put_u8(buf + 2, range);
-  put_u8(buf + 3, 0); // PADDING
-  ADVANCE(buf, size, 4);
+  byte *buf = sk->rbuf;
 
-  put_oid(buf, subtree, &size);
-  ADVANCE(buf, size, oid_size(subtree));
+  if (size < AGENTX_HEADER_SIZE)
+    return 0;
 
-  if (upp_b)
+  if (parse_response(p, buf, size))
   {
-    put_u32(buf, upp_b);
-    ADVANCE(buf, size, 4);
-  }
+    p->p.disabled = 1;
+    proto_notify_state(&p->p, PS_DOWN);
 
-  header_update_len(sk->tbuf, buf - sk->tbuf + AGENTX_HEADER_SIZE);
+    sk->tx_hook = NULL;
+    sk->rx_hook = NULL;
+  }
 
-  sk_send(sk, buf - sk->tbuf);
+  /* all done */
+  return 0;
 }
 
 /*
- * cont is optional context nullable
- * upp_b is upper_bond
- */
-int
-snmp_unregister_oid(sock *sk, struct oid *subtree, const char *cont, u32 upp_b)
+void
+snmp_agent_reconfigure(void)
 {
-  byte *buf = sk->tbuf;
-  uint size = sk->tbsize;
-  log(L_INFO "snmp_unregister_oid()");
 
-  u8 flags = AGENTX_NETWORK_BYTE_ORDER | ((cont) ? AGENTX_NON_DEFAULT_CONTEXT :
-0);
-  PASTE_HEADER(buf, AGENTX_UNREGISTER_PDU, flags, size);
+}
+*/
+
+static byte *
+find_bgp_one(struct bgp_proto *bp, struct oid *o, byte *buf,  uint size, uint contid)
+{
+  struct bgp_conn *b_conn = bp->conn;
+  struct bgp_conn *b_in = bp->incomming_conn;
+  struct bgp_conn *b_out = bp->outgoing_conn;
+
+  uint b_state;
+
+  if (b_conn)
+    b_state = b_conn->state;
+  else if (MAX(b_in->state, b_out->state) == BS_CLOSE &&
+    MIN(b_in->state, b_out->state) != BS_CLOSE)
+    b_state = MIN(b_in->state, b_out->state); 
+  /* BS_CLOSE is unsupported by BGP4-MIB */
+  else if (MIN(b_in->state, b_out->state) == BS_CLOSE)
+    b_state = BS_IDLE;
+  else
+    b_state = MAX(b_in->state, b_out->state);
+  
+  struct agentx_varbind *vb = (void *) buf;
+  
   
-  if (cont)
+  switch (o->ids[4])
   {
-    put_str(buf, cont, &size);
-    ADVANCE(buf, size, strlen(cont) + 4);
+    case SNMP_BGP_IDENTIFIER:
+      if (b_state == BS_ESTABLISHED)
+       STORE_PTR(pkt, ip_to_u32(b_conn->remote_ip));
+       BGP_DATA(vb, AGENTX_IP_ADDRESS, pkt);
+      else if (b_state == BS_OPENCONFIRM)
+       STORE_PTR(pkt, ip4_to_u32( (b_state == b_in->state) ? b_in->state : b_out->state));
+       BGP_DATA(vb, AGENTX_IP_ADDRESS, pkt);
+      else
+       STORE_PTR(pkt, ip4_to_u32(IPA_NONE));
+       BGP_DATA(vb, AGENTX_IP_ADDRESS, pkt);
+      
+    case SNMP_BGP_STATE: 
+      STORE_PTR(pkt, b_state);
+      BGP_DATA(vb, AGENTX_INTEGER, pkt);
+
+    case SNMP_BGP_ADMIN_STATUS:
+      STORE_PTR(pkt, (bp->disabled) ? AGENTX_ADMIN_STOP : AGENTX_ADMIN_START);
+      BGP_DATA(vb, AGENTX_INTEGER, pkt);
+
+    case SNMP_BGP_VERSION:
+      STORE(pkt, BGP4_VERSIONS);
+      STORE
+    case SNMP_BGP_LOCAL_ADDR:
+    case SNMP_BGP_LOCAL_PORT:
+    case SNMP_BGP_REMOTE_ADDR:
+    case SNMP_BGP_REMOTE_PORT:
+    case SNMP_BGP_REMOTE_AS:
+    case SNMP_BGP_RX_UPDATES:
+    case SNMP_BGP_TX_UPDATES:
+    case SNMP_BGP_RX_MESSAGES:
+    case SNMP_BGP_TX_MESSAGES:
+
+    case SNMP_BGP_FSM_TRANSITIONS:
+
+    case SNMP_BGP_RETRY_INTERVAL:
+    case SNMP_BGP_HOLD_TIME:
+    case SNMP_BGP_KEEPALIVE:
+    case SNMP_BGP_HOLD_TIME_CONFIGURED:
+    case SNMP_BGP_KEEPALIVE_CONFIGURED:
+
+    /* UNSUPPORTED */
+    case SNMP_BGP_LAST_ERROR:
+
+    case SNMP_BGP_FSM_ESTABLISHED_TIME:
+
+    case SNMP_BGP_ORIGINATION_INTERVAL:
+    case SNMP_BGP_MIN_ROUTE_ADVERTISEMENT:
+    case SNMP_BGP_MIN_UPDATE_ELAPSED_TIME:
+    defualt:
+      vb->type = AGENTX_NO_SUCH_OBJECT;
+      /* pkt += 0;  no data */
+      break;
   }
+}
 
-  put_oid(buf, subtree, &size);
-  ADVANCE(buf, size, oid_size(subtree));
+/* contid - context identification number */
+static byte *
+find_bgp_record(struct snmp_proto *p, struct oid *o, byte *buf, uint size, uint contid)
+{
+  struct agentx_varbind *vb = (void *) buf;
+  
+  if (o->n_subid < 3)
+  {
+    vb->type = AGENTX_NO_SUCH_OBJECT;
+    return buff + vb_size(vb);
+  }
 
-  if (upp_b)
+  byte *pkt = buf + vb_size(vb);
+  switch (o->ids[2])
   {
-    put_u32(buf, upp_b);
-    ADVANCE(buf, size, 4);
+    case BGP4_MIB_VERSION:
+      vb->type = AGENTX_OCTET_STRING;
+      STORE(pkt, BGP4_VERSIONS);
+      pkt += 4; 
+      break;
+      
+    case BGP4_MIB_LOCAL_AS:
+      vb->type = AGENTX_INTEGER;
+      // XXX local as to use
+      STORE(pkt, p->local_as);
+      pkt += 4;
+      break;
+
+    case BGP4_PEER_TABLE:
+      /* end part of .1.3.6.1.2.1.15.3.1.x.a.b.c.d */ 
+      if (o->n_subid < 9 || o->ids[3] != 1 
+         || o->ids[4] == 0 || o->ids[4] > 24)
+      {
+       vb->type = AGENTX_NO_SUCH_OBJECT;
+       return buff + vb_size(vb);
+      }
+
+      // TODO enumerate range requests
+      ip_addr addr = ipa_build4(o->ids[5], o->ids[6], o->ids[7], o->ids[8]);  
+      struct snmp_bgp_peer_entry *pe = 
+        HASH_FIND(p->bgp_entries, SNMP_HASH, addr);
+
+      struct bgp_proto *bp = NULL;
+      if (pe && pe->bond->proto->proto && 
+         ipa_equal(pe->bond->proto->proto->remote_ip, addr))
+      {
+       bp = pe->bond->proto->proto;    
+      }
+      else 
+      {
+       struct snmp_bond *b;
+       WALK_LIST(b, p->bgp_entries)
+         if (b->proto->proto &&
+             ipa_equal(b->proto->proto->remote_ip, addr))
+           bp = b->proto->proto; 
+      }
+
+      if (!bp)
+      {
+       vb->type = AGENTX_NO_SUCH_OBJECT;
+       /* pkt += 0; no data */
+       return pkt;     
+      }
+      
+      return find_bgp_one(bp, o, buf, size, contid); 
+      break;
+
+    default:
+      vb->type = AGENTX_NO_SUCH_OBJECT;
+      /* pkt += 0; no data */ 
+      break;
   }
 
-  sk_send(sk, buf - sk->tbuf);  
+  return pkt;
 }
 
-static void
-snmp_stop_ack(sock *sk, uint size)
+static byte *
+find_ospf_record(struct snmp_proto *p, struct oid *o, byte *buf, uint size)
 {
-  struct snmp_proto *p = sk->data;
-  byte *buf = sk->rbuf;
-  
-  if (size < AGENTX_HEADER_SIZE)
-    return 0; 
+  // TODO XXX
+  return NULL;
+}
 
-  if (parse_response(p, buf, size))
+static inline byte *
+find_prefixed(struct snmp_proto *p, struct oid *o, byte *buf, uint size)
+{
+  struct agetnx_varbind *vb = (void *) buf;
+
+  memcpy(&vb->name, o, oid_size(o));
+  
+                       /* SNMPv2   mgmt                      mib-2 */
+  if (o->n_subid < 2 || (o->prefix != 2 && o->ides[0] != 1))
   {
-    p->p.disabled = 1;
-    proto_notify_state(&p->p, PS_DOWN);
+    vb->type = AGENTX_NO_SUCH_OBJECT;
+    return buf + vb_size(vb);
   }
+  switch (o->ids[1])
+  {
+    case SNMP_BGP4_MIB:
+      return find_bgp_record(p, o, buf, size);
+
+    case SNMP_OSPFv3_MIB:
+      return find_ospf_record(p, o, buf, size);
+
+    /* the old OSPF */
+    case SNMP_OSPF_MIB:
+      log("too old OSPF oid request");
+      break;
+
+    default:
+      log(L_INFO "unsupported oid");
+      break;
+  }
+
+  vb->type = AGENTX_NO_SUCH_OBJECT;
+  return buf + vb_size(vb);
 }
-/*
-void
-snmp_agent_reconfigure(void)
+
+/* tests if there is present canonical "internet" prefix .1.3.6.1
+  and if so it shortens the oid to the ``prefix'' form */
+static int
+prefixize(struct oid *o, int byte_ord)
 {
+  const u32 prefix[] = {1, 3, 6, 1};
 
+  /* NETWORK_BYTE_ORDER */
+  if (byte_ord)
+    /* prefix len == 4 */
+    for (uint i = 0; i < 4; i++)
+      if (get_u32(&o->ids[i]) != prefix[i]) return 0;
+
+  else
+    /* prefix len == 4 */
+    for (uint i = 0; i < 4; i++)
+      if (o->ids[i] != prefix[i]) return 0;
+
+  o->n_subid -= 5;
+  o->prefix = o->ids[4];
+  /* n_subid contains number of elements, not bytes */
+  memmove(&o->ids, &o->ids[5], o->n_subid << 2);
+  return 1;
+}
+
+static inline byte *
+find_n_fill(struct snmp_proto *p, struct oid *o, byte *buf, uint size, int byte_ord)
+{
+  if (!o->prefix && prefixize(o, byte_ord))
+    find_prefixed(p, o, buf, size);
+  else if (o->prefix)
+    find_prefixed(p, o, buf, size);
+  else
+    return NULL;
+}
+
+static byte *
+prepare_response(struct snmp_proto *p, byte *buf, uint size, u16 err_no, u16 index)
+{
+  log(L_INFO "prepare_response()");
+
+  if (size < sizeof(struct agentx_response))
+    return NULL;
+
+  struct agentx_response *r = (void *) buf;
+  struct agentx_header *h = &r->h;
+
+  SNMP_B_HEADER(h, AGENTX_RESPONSE_PDU)
+  SNMP_SESSION(h, p)
+
+  /* protocol doesn't care about subagent upTime */
+  STORE(r->uptime, 0);
+  STORE_16(r->err, err_no);
+  STORE_16(r->index, index);
+
+  buf += sizeof(struct agentx_response);
+  return buf;
 }
-*/
 
 #undef SNMP_ERR_SHIFT
index 71320519a8c6b15d43d1a27b4d97948720c5d461..c34dfa4be655e0027b805450a4000938d0863c6a 100644 (file)
 
 void snmp_start_subagent(struct snmp_proto *p);
 void snmp_stop_subagent(struct snmp_proto *p);
+void snmp_ping(struct snmp_proto *p);
+
+#define AGENTX_VERSION              1
+
+#define SNMP_OSPF_MIB 14             /* part of oid .1.3.6.1.2.1.14 */
+#define SNMP_BGP4_MIB 15             /* part of oid .1.3.6.1.2.1.15 */
+#define SNMP_OSPFv3_MIB 192          /* part of oid .1.3.6.1.2.1.192 */
+
+enum agentx_type {
+  AGENTX_INTEGER           =   2;
+  AGENTX_OCTET_STRING      =   4;
+  AGENTX_NULL              =   5;
+  AGENTX_OBJECT_ID         =   6;
+  AGENTX_IP_ADDRESS        =  64;
+  AGENTX_COUNTER_32        =  65;
+  AGENTX_GAUGE_32          =  66;
+  AGENTX_TIME_TICKS        =  67;
+  AGENTX_OPAQUE                    =  68;
+  AGENTX_COUNTER_64        =  70;
+  AGENTX_NO_SUCH_OBJECT            = 128;
+  AGENTX_NO_SUCH_INSTANCE   = 129;
+  AGENTX_END_OF_MIB_VIEW    = 130;
+} PACKED;
 
-#define AGENTX_INTEGER           2
-#define AGENTX_OCTET_STRING      4
-#define AGENTX_NULL              5
-#define AGENTX_OBJECT_ID         6
-#define AGENTX_IP_ADDRESS       64
-#define AGENTX_COUNTER_32       65
-#define AGENTX_GAUGE_32                 66
-#define AGENTX_TIME_TICKS       67
-#define AGENTX_OPAQUE           68
-#define AGENTX_COUNTER_64       70
-#define AGENTX_NO_SUCH_OBJECT  128
-#define AGENTX_NO_SUCH_INSTANCE 129
-#define AGENTX_END_OF_MIB_VIEW 130
+#define AGENTX_ADMIN_STOP   1
+#define AGENTX_ADMIN_START  2
 
 #define AGENTX_PRIORITY                127
 #define MAX_STR 0xFFFFFFFF
 
-#define PASTE_HEADER_(buf, v, t, f, s)               \
-  memset(buf, 0, sizeof(struct agentx_header));              \
-  struct agentx_header *h = (void *) buf;            \
-  log(L_INFO "value : %d", (void *) h == buf? 1:0);   \
-  h->version = v;                                    \
-  h->type = t;                                       \
-  h->flags = f;                                              \
-  h->pad = 0;                                        \
-  ADVANCE(buf, s, sizeof(struct agentx_header));      \
+#define SNMP_NATIVE
+
+#ifdef SNMP_NATIVE
+#define STORE(v,c) (v) = (u32) (c)
+#define STORE_16(v,c) (v) = (u16) (c)
+#define STORE_PTR(v,c) *((u32 *) (v)) = (u32) (c)
+#define SNMP_UPDATE(h,l) \
+  STORE((h)->payload, l)
+
+#else
+#define STORE(v, c) put_u32(&v, c)
+#define STORE_16(v,c) put_u32(&v, c)
+#define STORE_PTR(v,c) put_u32(v, c)
+#define SNMP_UPDATE(h,l) \
+  STORE(h->payload, l)
+#endif
+
+/* storing byte (u8) is always the same */
+#define SNMP_HEADER_(h, v, t, f)  \
+  put_u8(&h->version, v);        \
+  put_u8(&h->type, t);           \
+  put_u8(&h->flags, f);                  \
+  put_u8(&h->pad, 0);
+
+#ifdef SNMP_NATIVE
+#define SNMP_HEADER(h,t,f)    SNMP_HEADER_(h, AGENTX_VERSION, t, f)
+#else
+#define SNMP_HEADER(h,t,f) \
+  SNMP_HEADER_(h, AGENTX_VERSION, t, f | SNMP_NETWORK_BYTE_ORDER)
+#endif
+
+#define SNMP_B_HEADER(h, t) SNMP_HEADER(h, t, AGENTX_FLAG_BLANK)
+
+#define SNMP_SESSION(h, p)                     \
+  STORE(h->session_id, p->session_id);         \
+  STORE(h->transaction_id, p->transaction_id); \
+  p->transaction_id++;                         \
+  STORE(h->packet_id, p->packet_id);
+
+#define SNMP_CREATE(b, t, n)  \
+  n = (void *) (b);          \
+  memset(n, 0, sizeof(t));    \
+  (b) += sizeof(t);
+
+#define LOAD(v, bo) ((bo) ? get_u32(&v) : (u32) (v))
+#define LOAD_16(v, bo) ((bo) ? get_u16(&v) : (u16) (v))
+#define LOAD_PTR(v, bo) ((bo) ? get_u32(v) : (u32) *(v))
+
+#define LOAD_STR(p, b, s, l, bo)    \
+  l = LOAD(*((u32 *) b), bo);      \
+  log(L_INFO "LOAD_STR(), %p %u", p->p.pool, l + 1); \
+  s = mb_allocz(p->p.pool, l + 1);  \
+  memcpy(s, b, l);                 \
+  b += str_size(s);
+
+#define SNMP_LOAD_CONTEXT(p, h, b, s, l)      \
+  if (h->flags & AGENTX_NON_DEFAULT_CONTEXT)  \
+    { log(L_INFO "encountered non-default context"); \
+    LOAD_STR(p, b, s, l, h->flags & AGENTX_NETWORK_BYTE_ORDER); }
+
+#define SNMP_COPY_OID(b, o) \
+  memcpy(b, o, oid_size(o));   \
+  b += oid_size(o);
 
-#define PASTE_HEADER(buf, t, f, s)     PASTE_HEADER_(buf, AGENTX_VERSION, t, f, s)
-#define U32_CPY(w, u) memcpy((w), (u), 4); ADVANCE((w), 4, 4);
+#define SNMP_COPY_VB(b, s, e)  \
+  memcpy(b, s, 4);             \
+  b += 4;                      \
+  SNMP_COPY_OID(b, &s->name)   \
+  SNMP_COPY_OID(b, e)
+
+#define BGP_DATA_(v, d, p, o) \
+  (v)->type = d;                 \
+  p += o;
+
+#define BGP_DATA(v, d, p) BGP_DATA(v, d, p, 4)
 
 struct agentx_header {
   u8 version;
@@ -50,17 +125,12 @@ struct agentx_header {
 
 #define AGENTX_HEADER_SIZE sizeof(struct agentx_header)
 
-struct subid{
-  u32 len;
-  u32 ids[];
-};
-
 struct oid {
   u8 n_subid;
   u8 prefix;
   u8 include;
   u8 pad;
-  struct subid subid;
+  u32 ids[];
 };
 
 struct agentx_varbind {
@@ -70,6 +140,7 @@ struct agentx_varbind {
   struct oid name;
 };
 
+/* this does not work */
 struct agentx_search_range {
   struct oid start;
   struct oid end;
@@ -82,7 +153,18 @@ struct agentx_response {
   u16 index;
 };
 
-#define AGENTX_VERSION               1
+struct agentx_close_pdu {
+  struct agentx_header h;
+  u8 reason;
+};
+
+struct agentx_un_register_pdu {
+  struct agentx_header h;
+  u8 timeout;
+  u8 priority;
+  u8 range_subid;
+  u8 padd;
+};
 
 enum agentx_pdu {
   AGENTX_OPEN_PDU              =  1,
@@ -108,6 +190,7 @@ enum agentx_pdu {
 #define AGENTX_FLAGS_MASK          0x1F
 
 enum agentx_flags {
+  AGENTX_FLAG_BLANK                = 0x00,
   AGENTX_FLAG_INSTANCE_REGISTRATION = 0x01,
   AGENTX_FLAG_NEW_INDEX                    = 0x02,
   AGENTX_FLAG_ANY_INDEX                    = 0x04,