zebra: ZEBRA_HELLO and mopping up routes (BZ#448)

ZEBRA_HELLO message is used by routing daemons to inform zebra
what type of routes daemon will be announcing to zebra. Also
zebra uses route_type_oaths array to track which daemon announces
which protocol. Zebra mops up routes if daemon didn't for some
reason.
diff --git a/lib/zclient.c b/lib/zclient.c
index 5815eaa..12d113e 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -318,6 +318,25 @@
   return zclient_send_message(zclient);
 }
 
+static int
+zebra_hello_send (struct zclient *zclient)
+{
+  struct stream *s;
+
+  if (zclient->redist_default)
+    {
+      s = zclient->obuf;
+      stream_reset (s);
+
+      zclient_create_header (s, ZEBRA_HELLO);
+      stream_putc (s, zclient->redist_default);
+      stream_putw_at (s, 0, stream_get_endp (s));
+      return zclient_send_message(zclient);
+    }
+
+  return 0;
+}
+
 /* Make connection to zebra daemon. */
 int
 zclient_start (struct zclient *zclient)
@@ -359,6 +378,8 @@
   /* Create read thread. */
   zclient_event (ZCLIENT_READ, zclient);
 
+  zebra_hello_send (zclient);
+
   /* We need router-id information. */
   zebra_message_send (zclient, ZEBRA_ROUTER_ID_ADD);
 
diff --git a/lib/zebra.h b/lib/zebra.h
index 4b4c7c0..ab072d6 100644
--- a/lib/zebra.h
+++ b/lib/zebra.h
@@ -417,7 +417,8 @@
 #define ZEBRA_ROUTER_ID_ADD               20
 #define ZEBRA_ROUTER_ID_DELETE            21
 #define ZEBRA_ROUTER_ID_UPDATE            22
-#define ZEBRA_MESSAGE_MAX                 23
+#define ZEBRA_HELLO                       23
+#define ZEBRA_MESSAGE_MAX                 24
 
 /* Marker value used in new Zserv, in the byte location corresponding
  * the command value in the old zserv header. To allow old and new
diff --git a/zebra/rib.h b/zebra/rib.h
index 887ed3c..20a206e 100644
--- a/zebra/rib.h
+++ b/zebra/rib.h
@@ -266,6 +266,7 @@
 extern void rib_sweep_route (void);
 extern void rib_close (void);
 extern void rib_init (void);
+extern unsigned long rib_score_proto (u_char proto);
 
 extern int
 static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname,
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 8228350..7263389 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -2881,7 +2881,41 @@
   rib_sweep_table (vrf_table (AFI_IP, SAFI_UNICAST, 0));
   rib_sweep_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0));
 }
-
+
+/* Remove specific by protocol routes from 'table'. */
+static unsigned long
+rib_score_proto_table (u_char proto, struct route_table *table)
+{
+  struct route_node *rn;
+  struct rib *rib;
+  struct rib *next;
+  unsigned long n = 0;
+
+  if (table)
+    for (rn = route_top (table); rn; rn = route_next (rn))
+      for (rib = rn->info; rib; rib = next)
+        {
+          next = rib->next;
+          if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+            continue;
+          if (rib->type == proto)
+            {
+              rib_delnode (rn, rib);
+              n++;
+            }
+        }
+
+  return n;
+}
+
+/* Remove specific by protocol routes. */
+unsigned long
+rib_score_proto (u_char proto)
+{
+  return  rib_score_proto_table (proto, vrf_table (AFI_IP,  SAFI_UNICAST, 0))
+         +rib_score_proto_table (proto, vrf_table (AFI_IP6, SAFI_UNICAST, 0));
+}
+
 /* Close RIB and clean up kernel routes. */
 static void
 rib_close_table (struct route_table *table)
diff --git a/zebra/zserv.c b/zebra/zserv.c
index d558b2d..ec8cbf2 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -64,6 +64,15 @@
   return 0;
 }
 
+/* When client connects, it sends hello message
+ * with promise to send zebra routes of specific type.
+ * Zebra stores a socket fd of the client into
+ * this array. And use it to clean up routes that
+ * client didn't remove for some reasons after closing
+ * connection.
+ */
+static int route_type_oaths[ZEBRA_ROUTE_MAX];
+
 static int
 zserv_flush_data(struct thread *thread)
 {
@@ -1071,6 +1080,49 @@
   return 0;
 }
 
+/* Tie up route-type and client->sock */
+static void
+zread_hello (struct zserv *client)
+{
+  /* type of protocol (lib/zebra.h) */
+  u_char proto;
+  proto = stream_getc (client->ibuf);
+
+  /* accept only dynamic routing protocols */
+  if ((proto < ZEBRA_ROUTE_MAX)
+  &&  (proto > ZEBRA_ROUTE_STATIC))
+    {
+      zlog_notice ("client %d says hello and bids fair to announce only %s routes",
+                    client->sock, zebra_route_string(proto));
+
+      /* if route-type was binded by other client */
+      if (route_type_oaths[proto])
+        zlog_warn ("sender of %s routes changed %c->%c",
+                    zebra_route_string(proto), route_type_oaths[proto],
+                    client->sock);
+
+      route_type_oaths[proto] = client->sock;
+    }
+}
+
+/* If client sent routes of specific type, zebra removes it
+ * and returns number of deleted routes.
+ */
+static void
+zebra_score_rib (int client_sock)
+{
+  int i;
+
+  for (i = ZEBRA_ROUTE_RIP; i < ZEBRA_ROUTE_MAX; i++)
+    if (client_sock == route_type_oaths[i])
+      {
+        zlog_notice ("client %d disconnected. %lu %s routes removed from the rib",
+                      client_sock, rib_score_proto (i), zebra_route_string (i));
+        route_type_oaths[i] = 0;
+        break;
+      }
+}
+
 /* Close zebra client. */
 static void
 zebra_client_close (struct zserv *client)
@@ -1079,6 +1131,7 @@
   if (client->sock)
     {
       close (client->sock);
+      zebra_score_rib (client->sock);
       client->sock = -1;
     }
 
@@ -1283,6 +1336,9 @@
     case ZEBRA_IPV4_IMPORT_LOOKUP:
       zread_ipv4_import_lookup (client, length);
       break;
+    case ZEBRA_HELLO:
+      zread_hello (client);
+      break;
     default:
       zlog_info ("Zebra received unknown command %d", command);
       break;
@@ -1352,6 +1408,7 @@
       return;
     }
 
+  memset (&route_type_oaths, 0, sizeof (route_type_oaths));
   memset (&addr, 0, sizeof (struct sockaddr_in));
   addr.sin_family = AF_INET;
   addr.sin_port = htons (ZEBRA_PORT);
@@ -1422,6 +1479,8 @@
       return;
     }
 
+  memset (&route_type_oaths, 0, sizeof (route_type_oaths));
+
   /* Make server socket. */
   memset (&serv, 0, sizeof (struct sockaddr_un));
   serv.sun_family = AF_UNIX;