[pim] T41 DONE ssmping support
diff --git a/pimd/TODO b/pimd/TODO
index dc2ece5..0818d9c 100644
--- a/pimd/TODO
+++ b/pimd/TODO
@@ -354,7 +354,16 @@
     http://www.ietf.org/internet-drafts/draft-ietf-mboned-lightweight-igmpv3-mldv2-05.txt
     http://www.ietf.org/html.charters/mboned-charter.html
 
-T41 ssmping
-    http://www.venaas.no/multicast/ssmping/
+T41 DONE ssmping support
+    See http://www.venaas.no/multicast/ssmping/
+
+    Example:
+
+    debug ssmpingd
+    
+    conf t
+     ip ssmpingd 1.1.1.1
+    
+    show ip ssmpingd
 
 -x-
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index a4073f5..eed7492 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -2143,7 +2143,7 @@
   struct ssmpingd_sock *ss;
   time_t                now;
 
-  vty_out(vty, "Source          Socket Uptime  Requests%s",
+  vty_out(vty, "Source          Socket Address          Port Uptime   Requests%s",
 	  VTY_NEWLINE);
 
   if (!qpim_ssmpingd_list)
@@ -2154,13 +2154,25 @@
   for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss)) {
     char source_str[100];
     char ss_uptime[10];
+    struct sockaddr_in bind_addr;
+    int len = sizeof(bind_addr);
+    char bind_addr_str[100];
 
     pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
+
+    if (pim_socket_getsockname(ss->sock_fd, (struct sockaddr *) &bind_addr, &len)) {
+      vty_out(vty, "%% Failure reading socket name for ssmpingd source %s on fd=%d%s",
+	      source_str, ss->sock_fd, VTY_NEWLINE);
+    }
+
+    pim_inet4_dump("<addr?>", bind_addr.sin_addr, bind_addr_str, sizeof(bind_addr_str));
     pim_time_uptime(ss_uptime, sizeof(ss_uptime), now - ss->creation);
 
-    vty_out(vty, "%-15s %6d %8s %8lld%s",
+    vty_out(vty, "%-15s %6d %-15s %5d %8s %8lld%s",
 	    source_str,
 	    ss->sock_fd,
+	    bind_addr_str,
+	    ntohs(bind_addr.sin_port),
 	    ss_uptime,
 	    ss->requests,
 	    VTY_NEWLINE);
@@ -4004,6 +4016,7 @@
   install_element (ENABLE_NODE, &show_ip_mroute_cmd);
   install_element (ENABLE_NODE, &show_ip_mroute_count_cmd);
   install_element (ENABLE_NODE, &show_ip_route_cmd);
+  install_element (ENABLE_NODE, &show_ip_ssmpingd_cmd);
   install_element (ENABLE_NODE, &show_debugging_cmd);
 
   install_element (ENABLE_NODE, &test_igmp_receive_report_cmd);
diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c
index c908e8b..2e78605 100644
--- a/pimd/pim_sock.c
+++ b/pimd/pim_sock.c
@@ -260,6 +260,29 @@
   char cbuf[1000];
   int err;
 
+  /*
+   * IP_PKTINFO / IP_RECVDSTADDR don't yield sin_port.
+   * Use getsockname() to get sin_port.
+   */
+  if (to) {
+    struct sockaddr_in si;
+    socklen_t si_len = sizeof(si);
+    
+    ((struct sockaddr_in *) to)->sin_family = AF_INET;
+
+    if (pim_socket_getsockname(fd, (struct sockaddr *) &si, &si_len)) {
+      ((struct sockaddr_in *) to)->sin_port        = ntohs(0);
+      ((struct sockaddr_in *) to)->sin_addr.s_addr = ntohl(0);
+    }
+    else {
+      ((struct sockaddr_in *) to)->sin_port = si.sin_port;
+      ((struct sockaddr_in *) to)->sin_addr = si.sin_addr;
+    }
+
+    if (tolen) 
+      *tolen = sizeof(si);
+  }
+
   memset(&msgh, 0, sizeof(struct msghdr));
   iov.iov_base = buf;
   iov.iov_len  = len;
@@ -291,6 +314,15 @@
 	*tolen = sizeof(struct sockaddr_in);
       if (ifindex)
 	*ifindex = i->ipi_ifindex;
+
+      if (to && PIM_DEBUG_PACKETS) {
+	char to_str[100];
+	pim_inet4_dump("<to?>", to->sin_addr, to_str, sizeof(to_str));
+	zlog_debug("%s: HAVE_IP_PKTINFO to=%s,%d",
+		   __PRETTY_FUNCTION__,
+		   to_str, ntohs(to->sin_port));
+      }
+
       break;
     }
 #endif
@@ -302,6 +334,15 @@
 	((struct sockaddr_in *) to)->sin_addr = *i;
       if (tolen)
 	*tolen = sizeof(struct sockaddr_in);
+
+      if (to && PIM_DEBUG_PACKETS) {
+	char to_str[100];
+	pim_inet4_dump("<to?>", to->sin_addr, to_str, sizeof(to_str));
+	zlog_debug("%s: HAVE_IP_RECVDSTADDR to=%s,%d",
+		   __PRETTY_FUNCTION__,
+		   to_str, ntohs(to->sin_port));
+      }
+
       break;
     }
 #endif
@@ -333,3 +374,16 @@
   
   return loop;
 }
+
+int pim_socket_getsockname(int fd, struct sockaddr *name, int *namelen)
+{
+  if (getsockname(fd, name, namelen)) {
+    int e = errno;
+    zlog_warn("Could not get Socket Name for socket fd=%d: errno=%d: %s",
+	      fd, errno, safe_strerror(errno));
+    errno = e;
+    return PIM_SOCK_ERR_NAME;
+  }
+
+  return PIM_SOCK_ERR_NONE;
+}
diff --git a/pimd/pim_sock.h b/pimd/pim_sock.h
index d355780..3f026dc 100644
--- a/pimd/pim_sock.h
+++ b/pimd/pim_sock.h
@@ -35,6 +35,7 @@
 #define PIM_SOCK_ERR_DSTADDR (-7) /* Outgoing interface option */
 #define PIM_SOCK_ERR_NONBLOCK_GETFL (-8) /* Get O_NONBLOCK */
 #define PIM_SOCK_ERR_NONBLOCK_SETFL (-9) /* Set O_NONBLOCK */
+#define PIM_SOCK_ERR_NAME    (-10) /* Socket name (getsockname) */
 
 int pim_socket_raw(int protocol);
 int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop);
@@ -51,4 +52,6 @@
 
 int pim_socket_mcastloop_get(int fd);
 
+int pim_socket_getsockname(int fd, struct sockaddr *name, int *namelen);
+
 #endif /* PIM_SOCK_H */
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));
diff --git a/pimd/pimd.c b/pimd/pimd.c
index 220604d..71b74e9 100644
--- a/pimd/pimd.c
+++ b/pimd/pimd.c
@@ -60,6 +60,7 @@
 int64_t                   qpim_rpf_cache_refresh_last =  0;
 struct in_addr            qpim_inaddr_any;
 struct list              *qpim_ssmpingd_list = 0;
+struct in_addr            qpim_ssmpingd_group_addr;
 
 static void pim_free()
 {
diff --git a/pimd/pimd.h b/pimd/pimd.h
index a2dc643..b42c54c 100644
--- a/pimd/pimd.h
+++ b/pimd/pimd.h
@@ -86,6 +86,7 @@
 int64_t                   qpim_rpf_cache_refresh_last;
 struct in_addr            qpim_inaddr_any;
 struct list              *qpim_ssmpingd_list; /* list of struct ssmpingd_sock */
+struct in_addr            qpim_ssmpingd_group_addr;
 
 #define PIM_JP_HOLDTIME (qpim_t_periodic * 7 / 2)