[pim] T41 DONE ssmping support
diff --git a/pimd/pim_ssmpingd.c b/pimd/pim_ssmpingd.c
index 4eb5c8d..6422cf6 100644
--- a/pimd/pim_ssmpingd.c
+++ b/pimd/pim_ssmpingd.c
@@ -32,11 +32,24 @@
 #include "pim_str.h"
 #include "pimd.h"
 
+static const char * const PIM_SSMPINGD_REPLY_GROUP = "232.43.211.234";
+
+enum {
+  PIM_SSMPINGD_REQUEST = 'Q',
+  PIM_SSMPINGD_REPLY   = 'A'
+};
+
 static void ssmpingd_read_on(struct ssmpingd_sock *ss);
 
 void pim_ssmpingd_init()
 {
+  int result;
+
   zassert(!qpim_ssmpingd_list);
+
+  result = inet_pton(AF_INET, PIM_SSMPINGD_REPLY_GROUP, &qpim_ssmpingd_group_addr);
+  
+  zassert(result > 0);
 }
 
 void pim_ssmpingd_destroy()
@@ -67,8 +80,9 @@
   XFREE(MTYPE_PIM_SSMPINGD, ss);
 }
 
-static int ssmpingd_socket(struct in_addr addr, int mttl)
+static int ssmpingd_socket(struct in_addr addr, int port, int mttl)
 {
+  struct sockaddr_in sockaddr;
   int fd;
 
   fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
@@ -78,21 +92,36 @@
     return -1;
   }
 
+  sockaddr.sin_family = AF_INET;
+  sockaddr.sin_addr   = addr;
+  sockaddr.sin_port   = htons(port);
+
+  if (bind(fd, &sockaddr, sizeof(sockaddr))) {
+    char addr_str[100];
+    pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+    zlog_warn("%s: bind(fd=%d,addr=%s,port=%d,len=%d) failure: errno=%d: %s",
+	      __PRETTY_FUNCTION__,
+	      fd, addr_str, port, sizeof(sockaddr),
+	      errno, safe_strerror(errno));
+    close(fd);
+    return -1;
+  }
+
   /* Needed to obtain destination address from recvmsg() */
   {
 #if defined(HAVE_IP_PKTINFO)
     /* Linux IP_PKTINFO */
     int opt = 1;
     if (setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt))) {
-      zlog_warn("Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
-		fd, errno, safe_strerror(errno));
+      zlog_warn("%s: could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
+		__PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
     }
 #elif defined(HAVE_IP_RECVDSTADDR)
     /* BSD IP_RECVDSTADDR */
     int opt = 1;
     if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) {
-      zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
-		fd, errno, safe_strerror(errno));
+      zlog_warn("%s: could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
+		__PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
     }
 #else
     zlog_err("%s %s: missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()",
@@ -121,6 +150,19 @@
     return -1;
   }
 
+  {
+    int loop = 0;
+    if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
+		   (void *) &loop, sizeof(loop))) {
+      zlog_warn("%s: could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s",
+		__PRETTY_FUNCTION__,
+		loop ? "enable" : "disable",
+		fd, errno, safe_strerror(errno));
+      close(fd);
+      return PIM_SOCK_ERR_LOOP;
+    }
+  }
+
   if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
 		 (void *) &addr, sizeof(addr))) {
     zlog_warn("%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
@@ -172,6 +214,34 @@
   ssmpingd_free(ss);
 }
 
+static void ssmpingd_sendto(struct ssmpingd_sock *ss,
+			    const char *buf,
+			    int len,
+			    struct sockaddr_in to)
+{
+  socklen_t tolen = sizeof(to);
+  int sent;
+
+  sent = sendto(ss->sock_fd, buf, len, MSG_DONTWAIT, &to, tolen);
+  if (sent != len) {
+    int e = errno;
+    char to_str[100];
+    pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
+    if (sent < 0) {
+      zlog_warn("%s: sendto() failure to %s,%d: fd=%d len=%d: errno=%d: %s",
+		__PRETTY_FUNCTION__,
+		to_str, ntohs(to.sin_port), ss->sock_fd, len,
+		e, safe_strerror(e));
+    }
+    else {
+      zlog_warn("%s: sendto() partial to %s,%d: fd=%d len=%d: sent=%d",
+		__PRETTY_FUNCTION__,
+		to_str, ntohs(to.sin_port), ss->sock_fd,
+		len, sent);
+    }
+  }
+}
+
 static int ssmpingd_read_msg(struct ssmpingd_sock *ss)
 {
   struct interface *ifp;
@@ -183,6 +253,8 @@
   char buf[1000];
   int len;
 
+  ++ss->requests;
+
   len = pim_socket_recvfromto(ss->sock_fd, buf, sizeof(buf),
 			      &from, &fromlen,
 			      &to, &tolen,
@@ -197,6 +269,24 @@
 
   ifp = if_lookup_by_index(ifindex);
 
+  if (buf[0] != PIM_SSMPINGD_REQUEST) {
+    char source_str[100];
+    char from_str[100];
+    char to_str[100];
+    pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
+    pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str));
+    pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
+    zlog_warn("%s: bad ssmping type=%d from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s",
+	      __PRETTY_FUNCTION__,
+	      buf[0],
+	      from_str, ntohs(from.sin_port),
+	      to_str, ntohs(to.sin_port),
+	      ifp ? ifp->name : "<iface?>",
+	      ifindex, ss->sock_fd,
+	      source_str);
+    return 0;
+  }
+
   if (PIM_DEBUG_SSMPINGD) {
     char source_str[100];
     char from_str[100];
@@ -204,13 +294,24 @@
     pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
     pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str));
     pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
-    zlog_debug("%s: ssmpingd on source %s: interface %s ifindex=%d received ssmping from %s to %s on fd=%d",
+    zlog_debug("%s: recv ssmping from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s",
 	       __PRETTY_FUNCTION__,
-	       source_str,
+	       from_str, ntohs(from.sin_port),
+	       to_str, ntohs(to.sin_port),
 	       ifp ? ifp->name : "<iface?>",
-	       ifindex, from_str, to_str, ss->sock_fd);
+	       ifindex, ss->sock_fd,
+	       source_str);
   }
 
+  buf[0] = PIM_SSMPINGD_REPLY;
+
+  /* unicast reply */
+  ssmpingd_sendto(ss, buf, len, from);
+
+  /* multicast reply */
+  from.sin_addr = qpim_ssmpingd_group_addr;
+  ssmpingd_sendto(ss, buf, len, from);
+
   return 0;
 }
 
@@ -259,7 +360,7 @@
     qpim_ssmpingd_list->del = (void (*)(void *)) ssmpingd_free;
   }
 
-  sock_fd = ssmpingd_socket(source_addr, 64 /* ttl */);
+  sock_fd = ssmpingd_socket(source_addr, /* port: */ 4321, /* mTTL: */ 64);
   if (sock_fd < 0) {
     char source_str[100];
     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));