IPv6 support for discovery protocol
authorChris Tallon <chris@vomp.tv>
Tue, 15 Oct 2019 12:58:44 +0000 (13:58 +0100)
committerChris Tallon <chris@vomp.tv>
Tue, 15 Oct 2019 12:58:44 +0000 (13:58 +0100)
Makefile
dsock6.c [new file with mode: 0644]
dsock6.h [new file with mode: 0644]
mvpserver.c
mvpserver.h
udp6replier.c [new file with mode: 0644]
udp6replier.h [new file with mode: 0644]
udpreplier.c

index b6a0d99ed9cb7b023341f01e053302757039c886..26b81702411160e0830ba214029ee93750b937d3 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -64,8 +64,8 @@ DEFINES += -DVOMPSERVER
 OBJS = $(PLUGIN).o
 
 # VOMP-INSERT
-OBJS += dsock.o mvpserver.o udpreplier.o bootpd.o tftpd.o i18n.o vompclient.o tcp.o \
-                   ringbuffer.o mvprelay.o vompclientrrproc.o \
+OBJS += dsock.o dsock6.o mvpserver.o udpreplier.o udp6replier.o bootpd.o tftpd.o i18n.o \
+                  vompclient.o tcp.o ringbuffer.o mvprelay.o vompclientrrproc.o \
                    config.o log.o thread.o tftpclient.o \
                    media.o responsepacket.o \
                    mediafile.o mediaplayer.o servermediafile.o serialize.o medialauncher.o \
diff --git a/dsock6.c b/dsock6.c
new file mode 100644 (file)
index 0000000..d7a3692
--- /dev/null
+++ b/dsock6.c
@@ -0,0 +1,229 @@
+/*
+    Copyright 2019 Chris Tallon
+
+    This file is part of VOMP.
+
+    VOMP is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    VOMP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <net/if.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <sys/ioctl.h>
+#include <vector>
+#include <algorithm>
+
+#include "dsock6.h"
+
+DatagramSocket6::DatagramSocket6()
+{
+  addrlen = sizeof(struct sockaddr_in6);
+  log = Log::getInstance();
+  initted = 0;
+}
+
+DatagramSocket6::~DatagramSocket6()
+{
+  shutdown();
+}
+
+bool DatagramSocket6::init(USHORT port)
+{
+  if ((socketnum = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1)
+  {
+    log->log("UDP6", Log::CRIT, "Socket error");
+    return false;
+  }
+
+  char* one = (char*)1;
+  if (setsockopt(socketnum, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)))
+  {
+    log->log("UDP6", Log::CRIT, "Reuse addr fail");
+    return false;
+  }
+
+  struct sockaddr_in6 saddr;
+  memset(&saddr, 0, sizeof(saddr));
+  saddr.sin6_family = AF_INET6;
+  saddr.sin6_port = htons(port);
+  saddr.sin6_addr = in6addr_any;
+
+  if (bind(socketnum, (struct sockaddr *)&saddr, addrlen) == -1)
+  {
+    log->log("UDP6", Log::CRIT, "Bind error %u", port);
+    close(socketnum);
+    return false;
+  }
+
+  struct ifaddrs* ifas;
+  if(getifaddrs(&ifas))
+  {
+    log->log("UDP6", Log::CRIT, "getifaddrs error");
+    close(socketnum);
+    return false;
+  }
+
+  struct if_nameindex* ifs = if_nameindex();
+  std::vector<unsigned int> mcastIndexes;
+
+  struct ifaddrs* ifaNext = ifas;
+  for(int i = 0; ifaNext; i++)
+  {
+    if ((ifaNext->ifa_flags & IFF_MULTICAST) && (ifaNext->ifa_addr->sa_family == AF_INET6))
+    {
+      for(int i = 0; ifs[i].if_index > 0; i++)
+      {
+        if (!strcmp(ifaNext->ifa_name, ifs[i].if_name))
+        {
+          mcastIndexes.push_back(ifs[i].if_index);
+          break;
+        }
+      }
+    }
+    ifaNext = ifaNext->ifa_next;
+  }
+
+  freeifaddrs(ifas);
+  if_freenameindex(ifs);
+
+
+  std::sort(mcastIndexes.begin(), mcastIndexes.end());
+  auto last = std::unique(mcastIndexes.begin(), mcastIndexes.end());
+  mcastIndexes.erase(last, mcastIndexes.end());
+
+
+  for(auto mif : mcastIndexes)
+  {
+    //log->log("UDP6", Log::DEBUG, "To listen on index %u", mif);
+
+    struct ipv6_mreq mGroup;
+    inet_pton(AF_INET6, "ff15:766f:6d70:2064:6973:636f:7665:7279", &mGroup.ipv6mr_multiaddr);
+    mGroup.ipv6mr_interface = mif;
+
+    if (!setsockopt(socketnum, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mGroup, sizeof(mGroup)))
+      log->log("UDP6", Log::DEBUG, "Listening on IF %u", mif);
+    else
+      log->log("UDP6", Log::ERR, "Cannot listen on IF %u", mif);
+  }
+
+
+  FD_ZERO(&readfds);
+  FD_SET(socketnum, &readfds);
+  tv.tv_sec = 0;
+  tv.tv_usec = 0;
+
+  initted = 1;
+  return true;
+}
+
+void DatagramSocket6::shutdown()
+{
+  if (initted) close(socketnum);
+  initted = 0;
+}
+
+unsigned char DatagramSocket6::waitforMessage(unsigned char how)
+{
+  if (!initted) return 0;
+
+  /* how = 0 - block
+     how = 1 - start new wait
+     how = 2 - continue wait
+  */
+
+  struct timeval* passToSelect = NULL;
+
+
+  if (how == 0)
+  {
+    passToSelect = NULL;
+  }
+  else if (how == 1)
+  {
+    tv.tv_sec = 1;
+    tv.tv_usec = 100000;
+    passToSelect = &tv;
+  }
+  else if (how == 2)
+  {
+    if ((tv.tv_sec == 0) && (tv.tv_usec == 0))  // protection in case timer = 0
+    {
+      tv.tv_sec = 1;
+      tv.tv_usec = 100000;
+    }
+    passToSelect = &tv;
+  }
+  FD_ZERO(&readfds);
+  FD_SET(socketnum, &readfds);
+
+  if (select(socketnum + 1, &readfds, NULL, NULL, passToSelect) <= 0) return 1;
+
+  struct sockaddr_in6 theirAddr;
+  if ((mlength = recvfrom(socketnum, buf, MAXBUFLEN, 0, (struct sockaddr *)&theirAddr, &addrlen)) == -1)
+  {
+    log->log("UDP6", Log::DEBUG, "recvfrom error");
+    return 0;
+  }
+  else
+  {
+    memset(&buf[mlength], 0, MAXBUFLEN - mlength);
+    inet_ntop(AF_INET6, &theirAddr.sin6_addr, fromIPA, 40);
+    fromPort = ntohs(theirAddr.sin6_port);
+    log->log("UDP", Log::DEBUG, "%s:%i received length %i", fromIPA, fromPort, mlength);
+    return 2;
+  }
+
+  /* Return 0, failure
+     Return 1, nothing happened, timer expired
+     Return 2, packet arrived (timer not expired)
+  */
+}
+
+void DatagramSocket6::send(const char *ipa, USHORT port, char *message, int length)
+{
+  int sentLength = 0;
+
+  struct sockaddr_in6 theirAddr;
+  memset(&theirAddr, 0, sizeof(struct sockaddr_in6));
+
+  theirAddr.sin6_family = AF_INET6;
+  theirAddr.sin6_port = htons(port);
+  inet_pton(AF_INET6, ipa, &theirAddr.sin6_addr);
+
+  sentLength = sendto(socketnum, message, length, 0, (struct sockaddr *)&theirAddr, addrlen);
+  if (sentLength == length)
+  {
+    log->log("UDP", Log::DEBUG, "%s:%u sent length %i", ipa, port, length);
+    return;
+  }
+
+  log->log("UDP", Log::DEBUG, "%s:%u send failed %i", ipa, port, length);
+
+  sentLength = sendto(socketnum, message, length, 0, (struct sockaddr *)&theirAddr, addrlen);
+  if (sentLength == length)
+  {
+    log->log("UDP", Log::DEBUG, "%s:%u sent length %i 2nd try", ipa, port, length);
+    return;
+  }
+
+  log->log("UDP", Log::DEBUG, "%s:%u send failed %i 2nd try", ipa, port, length);
+}
diff --git a/dsock6.h b/dsock6.h
new file mode 100644 (file)
index 0000000..190b5f6
--- /dev/null
+++ b/dsock6.h
@@ -0,0 +1,61 @@
+/*
+    Copyright 2019 Chris Tallon
+
+    This file is part of VOMP.
+
+    VOMP is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    VOMP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#ifndef DSOCK6_H
+#define DSOCK6_H
+
+#include <sys/socket.h>
+#include <sys/select.h>
+
+#include "defines.h"
+#include "log.h"
+
+#define MAXBUFLEN 2000
+
+class DatagramSocket6
+{
+  public:
+    DatagramSocket6();
+    ~DatagramSocket6();
+    bool init(USHORT port);
+    void shutdown();
+    unsigned char waitforMessage(unsigned char); // int =0-block =1-new wait =2-continue wait
+    void send(const char *, USHORT, char *, int); // send wants: IP Address ddn style, port,
+                                            // data, length of data
+
+    char*  getData()             { return buf; }      // returns a pointer to the data
+    char*  getFromIPA()          { return fromIPA; }  // returns a pointer to from IP address
+    USHORT getFromPort() const   { return fromPort; } // returns the sender port number
+    int    getDataLength() const { return mlength; }  // returns data length
+
+  private:
+    Log* log;
+    int initted;
+    int socketnum;                  // Socket descriptor
+
+    socklen_t addrlen;              // length of sockaddr struct
+    char buf[MAXBUFLEN];            // main data buffer
+    char fromIPA[40];               // from string (ip address)
+    USHORT fromPort;                // which port user sent on
+    int mlength;                    // length of message
+    struct timeval tv;
+    fd_set readfds;
+};
+
+#endif
index 15890fbefc34086597e0664a985c3280f91ec793..4ca1b99818a363f43fdfb18780f0c397b606b88f 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright 2004-2005 Chris Tallon
+    Copyright 2004-2019 Chris Tallon
 
     This file is part of VOMP.
 
@@ -14,8 +14,7 @@
     GNU General Public License for more details.
 
     You should have received a copy of the GNU General Public License
-    along with VOMP; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+    along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
 */
 
 #include "mvpserver.h"
@@ -25,6 +24,8 @@
 
 extern pthread_mutex_t threadClientMutex;
 
+#define UDP6PORT 51056
+
 MVPServer::MVPServer()
 {
   // MH in case anbody has a better position :-)
@@ -51,6 +52,7 @@ int MVPServer::stop()
   close(listeningSocket);
 
   udpr.shutdown();
+  udpr6.shutdown();
   bootpd.shutdown();
   tftpd.shutdown();
   mvprelay.shutdown();
@@ -177,6 +179,7 @@ int MVPServer::run(char* tconfigDir)
   if (fail) tcpServerPort = 3024;
   
   int udpSuccess = udpr.run(udpport, serverName, tcpServerPort);
+  int udp6Success = udpr6.run(UDP6PORT, serverName, tcpServerPort);
 
   delete[] serverName;
 
@@ -187,6 +190,13 @@ int MVPServer::run(char* tconfigDir)
     return 0;
   }
 
+  if (!udp6Success)
+  {
+    log.log("Main", Log::CRIT, "Could not start UDP6 replier");
+    stop();
+    return 0;
+  }
+
   // Read config and start bootp and tftp as appropriate
 
   char* configString;
index ed29c6570e2f0d56ebd30c28ad82bb3d91c5de6c..d19a6843414f292a57c08a77e737713d6c4d365d 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "defines.h"
 #include "udpreplier.h"
+#include "udp6replier.h"
 #include "mvprelay.h"
 #include "bootpd.h"
 #include "tftpd.h"
@@ -49,6 +50,7 @@ class MVPServer : public Thread
     Log log;
     Config config;
     UDPReplier udpr;
+    UDP6Replier udpr6;
     Bootpd bootpd;
     Tftpd tftpd;
     MVPRelay mvprelay;
diff --git a/udp6replier.c b/udp6replier.c
new file mode 100644 (file)
index 0000000..b20c427
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+    Copyright 2019 Chris Tallon
+
+    This file is part of VOMP.
+
+    VOMP is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    VOMP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include "vompclientrrproc.h"
+
+#include "udp6replier.h"
+
+UDP6Replier::UDP6Replier()
+{
+  message = NULL;
+  messageLen = 0;
+}
+
+UDP6Replier::~UDP6Replier()
+{
+  shutdown();
+}
+
+int UDP6Replier::shutdown()
+{
+  if (threadIsActive()) threadCancel();
+
+  if (message) delete[] message;
+  message = NULL;
+  return 1;
+}
+
+int UDP6Replier::run(USHORT port, char* serverName, USHORT serverPort)
+{
+  if (threadIsActive()) return 1;
+
+  /*
+  VOMP Discovery Protocol V1
+
+  Client transmits: "VDP-0001\0<6-byte MAC>"...
+  ... for IPv4: broadcasts on ports 51051-51055
+  ... for IPv6: multicasts to ff15:766f:6d70:2064:6973:636f:7665:7279 port 51056
+
+  Server responds:
+
+  Field 1 p0: 9 bytes "VDP-0002\0"
+  
+  Field 2 p9, 1 byte:
+
+  0 = no IP specified
+  4 = first 4 bytes of field 3 are IPv4 address of server
+  6 = field 3 16 bytes are IPv6 address of server
+
+  Field 3, p10, 16 bytes:
+  As described above. If field 2 is 0, this should be all zeros. If this is an IPv4 address, remaining bytes should be zeros.
+
+  Field 4 p26, 2 bytes:
+  Port number of server
+
+  Field 5 p28, 4 bytes:
+  VOMP protocol version (defined in vdr.cc)
+  
+  Field 6 p32, variable length
+  String of server name, null terminated
+  */
+  
+  messageLen = strlen(serverName) + 33;
+  message = new char[messageLen];
+  memset(message, 0, messageLen);
+  // by zeroing the packet, this sets no ip address return information
+  
+  strcpy(message, "VDP-0002");
+  
+  USHORT temp = htons(serverPort);
+  memcpy(&message[26], &temp, 2);
+  
+  ULONG temp2 = htonl(VompClientRRProc::getProtocolVersionMin());
+  memcpy(&message[28], &temp2, 4);
+  
+  strcpy(&message[32], serverName);
+  // Fix Me add also the maximum version somewhere
+  if (!ds.init(port))
+  {
+    shutdown();
+    return 0;
+  }
+
+  if (!threadStart())
+  {
+    shutdown();
+    return 0;
+  }
+
+  Log::getInstance()->log("UDP6Replier", Log::DEBUG, "UDP replier started");
+  return 1;
+}
+
+void UDP6Replier::threadMethod()
+{
+  int retval;
+  while(1)
+  {
+    retval = ds.waitforMessage(0);
+    if (retval == 1) continue;
+
+    if (!strncmp(ds.getData(), "VDP-0001", 8))
+    {
+      Log::getInstance()->log("UDP6Replier", Log::DEBUG, "UDP6 request from %s", ds.getFromIPA());
+      ds.send(ds.getFromIPA(), ds.getFromPort(), message, messageLen);
+    }
+  }
+}
diff --git a/udp6replier.h b/udp6replier.h
new file mode 100644 (file)
index 0000000..09da833
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+    Copyright 2019 Chris Tallon
+
+    This file is part of VOMP.
+
+    VOMP is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    VOMP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#ifndef UDP6REPLIER_H
+#define UDP6REPLIER_H
+
+#include "log.h"
+#include "dsock6.h"
+#include "thread.h"
+
+class UDP6Replier : public Thread
+{
+  public:
+    UDP6Replier();
+    virtual ~UDP6Replier();
+
+    int run(USHORT udpPort, char* serverName, USHORT serverPort);
+    int shutdown();
+
+  private:
+    void threadMethod();
+
+    DatagramSocket6 ds;
+    char* message;
+    int messageLen;
+};
+
+#endif
index 88f83e4ced65387d5a71290508033d92982fe2a2..b5c404c290ea25165e5d37b3f23bceefb6cab798 100644 (file)
@@ -49,27 +49,29 @@ int UDPReplier::run(USHORT port, char* serverName, USHORT serverPort)
   /*
   VOMP Discovery Protocol V1
 
-  Client transmits: "VDP-0001\0<6-byte MAC>" on ports 51051-51055
+  Client transmits: "VDP-0001\0<6-byte MAC>"...
+  ... for IPv4: broadcasts on ports 51051-51055
+  ... for IPv6: multicasts to ff15:766f:6d70:2064:6973:636f:7665:7279 port 51051
 
   Server responds:
 
   Field 1 p0: 9 bytes "VDP-0002\0"
-  
+
   Field 2 p9, 1 byte:
 
   0 = no IP specified
-  4 = first 4 bytes of field 2 are IPv4 address of server
-  6 = field 2 16 bytes are IPv6 address of server
+  4 = first 4 bytes of field 3 are IPv4 address of server
+  6 = field 3 16 bytes are IPv6 address of server
 
   Field 3, p10, 16 bytes:
-  As described above. If field 1 is 0, this should be all zeros. If this is an IPv4 address, remaining bytes should be zeros.
+  As described above. If field 2 is 0, this should be all zeros. If this is an IPv4 address, remaining bytes should be zeros.
 
   Field 4 p26, 2 bytes:
   Port number of server
 
   Field 5 p28, 4 bytes:
   VOMP protocol version (defined in vdr.cc)
-  
+
   Field 6 p32, variable length
   String of server name, null terminated
   */