- Adapt MDS with this SNA implementation.
---
 src/base/Makefile.am             |   6 +-
 src/base/sna.h                   | 126 +++++++++++++++++++++++++++++++
 src/base/tests/sna_test.cc       | 117 ++++++++++++++++++++++++++++
 src/mds/mds_tipc_fctrl_intf.cc   |   3 +-
 src/mds/mds_tipc_fctrl_portid.cc |  45 ++++++-----
 src/mds/mds_tipc_fctrl_portid.h  |  79 +++----------------
 6 files changed, 280 insertions(+), 96 deletions(-)
 create mode 100644 src/base/sna.h
 create mode 100644 src/base/tests/sna_test.cc

diff --git a/src/base/Makefile.am b/src/base/Makefile.am
index 025fb86a2..5082175cf 100644
--- a/src/base/Makefile.am
+++ b/src/base/Makefile.am
@@ -173,7 +173,8 @@ noinst_HEADERS += \
        src/base/unix_client_socket.h \
        src/base/unix_server_socket.h \
        src/base/unix_socket.h \
-       src/base/usrbuf.h
+       src/base/usrbuf.h \
+       src/base/sna.h
 
 TESTS += bin/testleap bin/libbase_test bin/core_common_test
 
@@ -237,7 +238,8 @@ bin_libbase_test_SOURCES = \
        src/base/tests/time_compare_test.cc \
        src/base/tests/time_convert_test.cc \
        src/base/tests/time_subtract_test.cc \
-       src/base/tests/unix_socket_test.cc
+       src/base/tests/unix_socket_test.cc \
+       src/base/tests/sna_test.cc
 
 bin_libbase_test_LDADD = \
        $(GTEST_DIR)/lib/libgtest.la \
diff --git a/src/base/sna.h b/src/base/sna.h
new file mode 100644
index 000000000..b231fb134
--- /dev/null
+++ b/src/base/sna.h
@@ -0,0 +1,126 @@
+/*      -*- OpenSAF  -*-
+ *
+ * Copyright Ericsson AB 2019 - All Rights Reserved.
+ *
+ * This program 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. This file and program are licensed
+ * under the GNU Lesser General Public License Version 2.1, February 1999.
+ * The complete license can be accessed from the following location:
+ * http://opensource.org/licenses/lgpl-license.php
+ * See the Copying file included with the OpenSAF distribution for full
+ * licensing terms.
+ *
+ * Reference: Serial Number Arithmetic from RFC1982
+ *
+ */
+
+#ifndef BASE_SNA_H_
+#define BASE_SNA_H_
+
+#include <typeinfo>
+#include <stdexcept>
+
+#define MAX_16BITS 65536  // 2^16
+#define MAX_32BITS 4294967296  // 2^32
+
+template <class T>
+class _sna {
+ private:
+  T i;
+  uint64_t max() {
+    if (typeid(T) == typeid(uint64_t)) {
+      return MAX_32BITS;
+    } else if (typeid(T) == typeid(uint32_t)) {
+      return MAX_16BITS;
+    } else {
+      printf("Type:%s\n", typeid(T).name());
+      throw std::out_of_range("Invalid type");
+    }
+  }
+
+ public:
+  _sna(): i(0) {}
+  _sna(const _sna &t) {
+    i = t.i;
+  }
+  explicit _sna(const uint64_t &n) {
+    if ((n < 0) || (n > (max()-1)))
+      throw std::out_of_range("SNA assign with invalid value");
+    i = n;
+  }
+  _sna& operator=(const _sna &t) {
+    // check for self-assignment
+    if (&t == this)
+      return *this;
+    i = t.i;
+    return *this;
+  }
+  T v() const {
+    return i;
+  }
+  _sna& operator+=(const uint64_t& n) {
+    if ((n < 0) || (n > (max()/2 - 1)))
+      throw std::out_of_range("SNA received invalid addition value");
+    i = (i + n) % max();
+    return *this;
+  }
+  friend _sna operator+(_sna m, const uint64_t& n) {
+    m += n;
+    return m;
+  }
+  // prefix ++
+  _sna& operator++() {
+    *this += 1;
+    return *this;
+  }
+  // postfix ++
+  _sna operator++(int) {
+    _sna tmp(*this);
+    operator++();
+    return tmp;
+  }
+  bool operator==(const _sna& rhs) {
+    return i == rhs.i;
+  }
+  bool operator==(const uint32_t val) {
+    return i == val;
+  }
+  bool operator!=(const _sna& rhs) {
+    return i != rhs.i;
+  }
+  bool operator<(const _sna& rhs) {
+    return (i < rhs.i && rhs.i - i < max()/2) || \
+           (i > rhs.i && i - rhs.i > max()/2);
+  }
+  bool operator>=(const _sna& rhs) {
+    return !(*this < rhs);
+  }
+  bool operator>(const _sna& rhs) {
+    return (i < rhs.i && rhs.i - i > max()/2) || \
+           (i > rhs.i && i - rhs.i < max()/2);
+  }
+  bool operator<=(const _sna& rhs) {
+    return !(*this > rhs);
+  }
+  int64_t operator-(const _sna& rhs) {
+    if (*this >= rhs) {
+      if (i >= rhs.v()) {
+        return i - rhs.v();
+      } else {
+        return (i + max()) - rhs.v();
+      }
+    } else {
+      if (i < rhs.v()) {
+        return i - rhs.v();
+      } else {
+        return i - (rhs.v() + max());
+      }
+    }
+  }
+};
+
+typedef _sna<uint32_t> sna16_t;
+typedef _sna<uint64_t> sna32_t;
+
+#endif  // BASE_SNA_H_
diff --git a/src/base/tests/sna_test.cc b/src/base/tests/sna_test.cc
new file mode 100644
index 000000000..4b7eb83e3
--- /dev/null
+++ b/src/base/tests/sna_test.cc
@@ -0,0 +1,117 @@
+/*      -*- OpenSAF  -*-
+ *
+ * Copyright Ericsson AB 2019 - All Rights Reserved.
+ *
+ * This program 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. This file and program are licensed
+ * under the GNU Lesser General Public License Version 2.1, February 1999.
+ * The complete license can be accessed from the following location:
+ * http://opensource.org/licenses/lgpl-license.php
+ * See the Copying file included with the OpenSAF distribution for full
+ * licensing terms.
+ *
+ * Reference: Serial Number Arithmetic from RFC1982
+ *
+ */
+
+#include "base/sna.h"
+#include "gtest/gtest.h"
+
+template <class T>
+int test_sna(T x) {
+  int rc = 1;
+  printf("\n============= START with x=%lu =============\n", (uint64_t)x.v());
+  T y = x;
+  printf("x=%lu, y=%lu: check x == y++ is TRUE\n",
+    (uint64_t)x.v(), (uint64_t)y.v());
+  if (x == y++) {
+    printf("now y=%lu, reset y = x\n", (uint64_t)y.v());
+    y = x;
+    printf("x=%lu, y=%lu: check x != ++y is TRUE\n",
+      (uint64_t)x.v(), (uint64_t)y.v());
+    if (x != ++y) {
+      printf("now y=%lu, reset y = x\n", (uint64_t)y.v());
+      y = x;
+      printf("x=%lu, y=%lu: check x < ++y is TRUE\n",
+        (uint64_t)x.v(), (uint64_t)y.v());
+      if (x < ++y) {
+        printf("x=%lu: check x + 1 > x and x + 1 >= x is TRUE\n",
+          (uint64_t)x.v());
+        if ((x + 1 > x) && (x + 1 >= x)) {
+          printf("x=%lu: check x < x + 1 and x <= x + 1 is TRUE\n",
+            (uint64_t)x.v());
+          y = x + 1;
+          printf("y = x+1 => y=%lu\n", (uint64_t)y.v());
+          y = y + 1;
+          printf("y = y+1 => y=%lu\n", (uint64_t)y.v());
+          if ((x < x + 1) && (x <= x + 1)) {
+            try {
+              printf("x=%lu: add invalid (-1)\n", (uint64_t)x.v());
+              x = x + (-1);
+            } catch (const std::out_of_range& oor) {
+              printf("Expected error: %s\n", oor.what());
+              try {
+                uint64_t max_value = 0;
+                if (typeid(T) == typeid(sna16_t))
+                  max_value = MAX_16BITS;
+                else if (typeid(T) == typeid(sna32_t))
+                  max_value = MAX_32BITS;
+                printf("x=%lu: add invalid (%lu)\n",
+                  (uint64_t)x.v(), max_value);
+                x = x + max_value;
+              } catch (const std::out_of_range& oor) {
+                printf("Expected error: %s\n", oor.what());
+                rc = 0;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+  printf("================ END with x=%lu ==============\n", (uint64_t)x.v());
+  return rc;
+}
+
+
+class SnaTest : public ::testing::Test {
+ protected:
+  SnaTest() {}
+  virtual ~SnaTest() {
+    // Cleanup work that doesn't throw exceptions here.
+  }
+  virtual void SetUp() {
+    // Code here will be called immediately after the constructor (right
+    // before each test)
+  }
+  virtual void TearDown() {}
+};
+
+TEST_F(SnaTest, unit16_sna) {
+  sna16_t x;
+  EXPECT_EQ(0, test_sna(x));
+  sna16_t x1 = sna16_t(1);
+  sna16_t x2 = sna16_t(MAX_16BITS - 1);
+  EXPECT_EQ(2, x1 - x2);
+  EXPECT_EQ(-2, x2 - x1);
+  EXPECT_EQ(0, test_sna(x1));
+  EXPECT_EQ(0, test_sna(x2));
+}
+
+TEST_F(SnaTest, unit32_sna) {
+  sna32_t x;
+  EXPECT_EQ(0, test_sna(x));
+  sna32_t x1 = sna32_t(1);
+  sna32_t x2 = sna32_t(MAX_32BITS - 1);
+  EXPECT_EQ(2, x1 - x2);
+  EXPECT_EQ(-2, x2 - x1);
+  EXPECT_EQ(0, test_sna(x1));
+  EXPECT_EQ(0, test_sna(x2));
+}
+
+
+int main(int argc, char **argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/src/mds/mds_tipc_fctrl_intf.cc b/src/mds/mds_tipc_fctrl_intf.cc
index fe3dbd597..dbf9d8e4a 100644
--- a/src/mds/mds_tipc_fctrl_intf.cc
+++ b/src/mds/mds_tipc_fctrl_intf.cc
@@ -104,7 +104,8 @@ void process_timer_event(const Event& evt) {
   bool txprob_restart = false;
   for (auto i : portid_map) {
     TipcPortId* portid = i.second;
-
+    if (!portid)
+      continue;
     if (evt.type_ == Event::Type::kEvtTmrTxProb) {
       if (portid->ReceiveTmrTxProb(kTxProbMaxRetries) == true) {
         txprob_restart = true;
diff --git a/src/mds/mds_tipc_fctrl_portid.cc b/src/mds/mds_tipc_fctrl_portid.cc
index 3704baddb..9fc9a50a5 100644
--- a/src/mds/mds_tipc_fctrl_portid.cc
+++ b/src/mds/mds_tipc_fctrl_portid.cc
@@ -57,22 +57,22 @@ void MessageQueue::Queue(DataMessage* msg) {
   queue_.push_back(msg);
 }
 
-DataMessage* MessageQueue::Find(Seq16 fseq) {
+DataMessage* MessageQueue::Find(sna16_t fseq) {
   for (const auto& it : queue_) {
     DataMessage *m = it;
-    if (Seq16(m->header_.fseq_) == fseq) {
+    if (sna16_t(m->header_.fseq_) == fseq) {
       return m;
     }
   }
   return nullptr;
 }
 
-uint64_t MessageQueue::Erase(Seq16 fseq_from, Seq16 fseq_to) {
+uint64_t MessageQueue::Erase(uint16_t chksize, sna16_t fseq_to) {
   uint64_t msg_len = 0;
   for (auto it = queue_.begin(); it != queue_.end();) {
     DataMessage *m = *it;
-    if (fseq_from <= Seq16(m->header_.fseq_) &&
-        Seq16(m->header_.fseq_) <= fseq_to) {
+    if (sna16_t(m->header_.fseq_) + chksize > fseq_to &&
+        sna16_t(m->header_.fseq_) <= fseq_to) {
       msg_len += m->header_.msg_len_;
       it = queue_.erase(it);
       delete m;
@@ -93,10 +93,10 @@ DataMessage* MessageQueue::FirstUnsent() {
   return nullptr;
 }
 
-void MessageQueue::MarkUnsentFrom(Seq16 fseq) {
+void MessageQueue::MarkUnsentFrom(sna16_t fseq) {
   for (const auto& it : queue_) {
     DataMessage *m = it;
-    if (Seq16(m->header_.fseq_) >= fseq) m->is_sent_ = false;
+    if (sna16_t(m->header_.fseq_) >= fseq) m->is_sent_ = false;
   }
 }
 
@@ -315,7 +315,7 @@ uint32_t TipcPortId::ReceiveData(uint32_t mseq, uint16_t 
mfrag,
   }
 
   // update receiver sequence window
-  if (rcvwnd_.acked_ < Seq16(fseq) && rcvwnd_.rcv_ + Seq16(1) == Seq16(fseq)) {
+  if (rcvwnd_.acked_ < sna16_t(fseq) && rcvwnd_.rcv_ + 1 == sna16_t(fseq)) {
     m_MDS_LOG_DBG("FCTRL: [me] <-- [node:%x, ref:%u], "
         "RcvData[mseq:%u, mfrag:%u, fseq:%u], "
         "rcvwnd[acked:%u, rcv:%u, nacked:%" PRIu64 "]",
@@ -346,7 +346,7 @@ uint32_t TipcPortId::ReceiveData(uint32_t mseq, uint16_t 
mfrag,
     // It is not used for now, so ignore it.
 
     // check for transmission error
-    if (rcvwnd_.rcv_ + Seq16(1) < Seq16(fseq)) {
+    if (rcvwnd_.rcv_ + 1 < sna16_t(fseq)) {
       if (rcvwnd_.rcv_ == 0 && rcvwnd_.acked_ == 0) {
         // peer does not realize that this portid reset
         m_MDS_LOG_ERR("FCTRL: [me] <-- [node:%x, ref:%u], "
@@ -357,7 +357,7 @@ uint32_t TipcPortId::ReceiveData(uint32_t mseq, uint16_t 
mfrag,
             mseq, mfrag, fseq,
             rcvwnd_.acked_.v(), rcvwnd_.rcv_.v(), rcvwnd_.nacked_space_);
 
-        rcvwnd_.rcv_ = fseq;
+        rcvwnd_.rcv_ = sna16_t(fseq);
       } else {
         rc = NCSCC_RC_FAILURE;
         // msg loss
@@ -369,9 +369,9 @@ uint32_t TipcPortId::ReceiveData(uint32_t mseq, uint16_t 
mfrag,
             mseq, mfrag, fseq,
             rcvwnd_.acked_.v(), rcvwnd_.rcv_.v(), rcvwnd_.nacked_space_);
         // send nack
-        SendNack((rcvwnd_.rcv_ + Seq16(1)).v(), svc_id);
+        SendNack((rcvwnd_.rcv_ + 1).v(), svc_id);
       }
-    } else if (Seq16(fseq) <= rcvwnd_.rcv_) {
+    } else if (sna16_t(fseq) <= rcvwnd_.rcv_) {
       rc = NCSCC_RC_FAILURE;
       // unexpected retransmission
       m_MDS_LOG_ERR("FCTRL: [me] <-- [node:%x, ref:%u], "
@@ -406,7 +406,7 @@ void TipcPortId::ReceiveChunkAck(uint16_t fseq, uint16_t 
chksize) {
         txprob_cnt_, (uint8_t)state_);
   }
   // update sender sequence window
-  if (sndwnd_.acked_ < Seq16(fseq)) {
+  if (sndwnd_.acked_ < sna16_t(fseq)) {
     // The fseq_ should always be less then sndwnd_.send_, hence
     // mds should check the sender being capable of sending more
     // message only if D = sndwnd_.send_ - sndwnd_.acked_ < 2^15 - 1 = 32767
@@ -416,7 +416,7 @@ void TipcPortId::ReceiveChunkAck(uint16_t fseq, uint16_t 
chksize) {
     // For now mds logs a warning and let the transmission continue
     // (mds could be changed to return try again if it is not a backward
     // compatibility problem to a specific client).
-    if (Seq16(fseq) >= sndwnd_.send_) {
+    if (sna16_t(fseq) >= sndwnd_.send_) {
       m_MDS_LOG_ERR("FCTRL: [me] <-- [node:%x, ref:%u], "
           "RcvChkAck[fseq:%u, chunk:%u], "
           "sndwnd[acked:%u, send:%u, nacked:%" PRIu64 "], "
@@ -438,12 +438,11 @@ void TipcPortId::ReceiveChunkAck(uint16_t fseq, uint16_t 
chksize) {
         sndqueue_.Size());
 
     // fast forward the sndwnd_.acked_ sequence to fseq
-    sndwnd_.acked_ = fseq;
+    sndwnd_.acked_ = sna16_t(fseq);
 
     // remove a number @chksize messages out of sndqueue_ and decrease
     // the nacked_space_ of sender
-    uint64_t acked_bytes = sndqueue_.Erase(Seq16(fseq) - (chksize-1),
-        Seq16(fseq));
+    uint64_t acked_bytes = sndqueue_.Erase(chksize, sna16_t(fseq));
     sndwnd_.nacked_space_ -= acked_bytes;
 
     // try to send a few pending msg
@@ -503,8 +502,8 @@ void TipcPortId::ReceiveNack(uint32_t mseq, uint16_t mfrag,
     return;
   }
   if (state_ == State::kRcvBuffOverflow) {
-    sndqueue_.MarkUnsentFrom(Seq16(fseq));
-    if (Seq16(fseq) - sndwnd_.acked_ > 1) {
+    sndqueue_.MarkUnsentFrom(sna16_t(fseq));
+    if (sna16_t(fseq) - sndwnd_.acked_ > 1) {
       m_MDS_LOG_ERR("FCTRL: [me] <-- [node:%x, ref:%u], "
           "RcvNack[fseq:%u], "
           "sndwnd[acked:%u, send:%u, nacked:%" PRIu64 "], "
@@ -520,9 +519,9 @@ void TipcPortId::ReceiveNack(uint32_t mseq, uint16_t mfrag,
     state_ = State::kRcvBuffOverflow;
     m_MDS_LOG_NOTIFY("FCTRL: [node:%x, ref:%u] --> Overflow ",
         id_.node, id_.ref);
-    sndqueue_.MarkUnsentFrom(Seq16(fseq));
+    sndqueue_.MarkUnsentFrom(sna16_t(fseq));
   }
-  DataMessage* msg = sndqueue_.Find(Seq16(fseq));
+  DataMessage* msg = sndqueue_.Find(sna16_t(fseq));
   if (msg != nullptr) {
     // Resend the msg found
     if (Send(msg->msg_data_, msg->header_.msg_len_) == NCSCC_RC_SUCCESS) {
@@ -546,8 +545,8 @@ void TipcPortId::ReceiveNack(uint32_t mseq, uint16_t mfrag,
 bool TipcPortId::ReceiveTmrTxProb(uint8_t max_txprob) {
   bool restart_txprob = false;
   if (state_ == State::kDisabled ||
-      sndwnd_.acked_ > Seq16(1) ||
-      rcvwnd_.rcv_ > Seq16(1)) return restart_txprob;
+      sndwnd_.acked_ > sna16_t(1) ||
+      rcvwnd_.rcv_ > sna16_t(1)) return restart_txprob;
   if (state_ == State::kTxProb || state_ == State::kRcvBuffOverflow) {
     txprob_cnt_++;
     if (txprob_cnt_ >= max_txprob) {
diff --git a/src/mds/mds_tipc_fctrl_portid.h b/src/mds/mds_tipc_fctrl_portid.h
index 24fb195d2..7f48327f2 100644
--- a/src/mds/mds_tipc_fctrl_portid.h
+++ b/src/mds/mds_tipc_fctrl_portid.h
@@ -24,82 +24,21 @@
 #include <stdio.h>
 #include <unistd.h>
 #include <deque>
+#include "base/sna.h"
 #include "mds/mds_tipc_fctrl_msg.h"
 
 namespace mds {
 
-class Seq16 {
- public:
-#define SEQ16_MAX 65536
-#define SEQ16_SPACE 32768
-  uint16_t value_;
-  explicit Seq16(uint16_t v) {
-    value_ = uint16_t((uint32_t)v % SEQ16_MAX);
-  }
-  uint16_t v() {
-    return value_;
-  }
-  Seq16 operator + (const Seq16 add) const {
-    return Seq16(((uint32_t)value_ + (uint32_t)add.value_) % SEQ16_MAX);
-  }
-
-  int16_t operator - (const Seq16 sub) const {
-    if (value_ < sub.value_ && (sub.value_ - value_ < SEQ16_SPACE)) {
-      return value_ - sub.value_;
-    }
-    if (value_ > sub.value_ && (value_ - sub.value_ > SEQ16_SPACE)) {
-      return (int32_t)value_ + SEQ16_MAX -  (int32_t)sub.value_;
-    }
-    if (value_ < sub.value_ && (sub.value_ - value_ > SEQ16_SPACE)) {
-      return (int32_t)value_ + SEQ16_MAX -  (int32_t)sub.value_;
-    }
-    if (value_ > sub.value_ && (value_ - sub.value_ < SEQ16_SPACE)) {
-      return value_ - sub.value_;
-    }
-    return 0;
-  }
-  Seq16 operator - (const uint16_t sub) const {
-    return Seq16(((uint32_t)value_ + 65536 - sub) % SEQ16_MAX);
-  }
-  void operator ++() {
-    value_ = (value_ + 1) % SEQ16_MAX;
-  }
-  void operator = (const uint16_t v) {
-    value_ = v % SEQ16_MAX;
-  }
-  bool operator == (const Seq16& seq) const {
-    return value_ == seq.value_;
-  }
-  bool operator == (uint16_t val) const {
-    return value_ == val;
-  }
-  bool operator <= (const Seq16& seq) {
-    return *this == seq || *this < seq;
-  }
-  bool operator < (const Seq16& seq) {
-    if (value_ < seq.value_ && (seq.value_ - value_ < SEQ16_SPACE)) return 
true;
-    if (value_ > seq.value_ && (value_ - seq.value_ > SEQ16_SPACE)) return 
true;
-    return false;
-  }
-  bool operator > (const Seq16& seq) {
-    if (value_ < seq.value_ && (seq.value_ - value_ > SEQ16_SPACE)) return 
true;
-    if (value_ > seq.value_ && (value_ - seq.value_ < SEQ16_SPACE)) return 
true;
-    return false;
-  }
-  bool operator >= (const Seq16& seq) {
-    return *this == seq || *this > seq;
-  }
-};
-
 class MessageQueue {
  public:
   void Queue(DataMessage* msg);
-  DataMessage* Find(Seq16 fseq);
-  uint64_t Erase(Seq16 fseq_from, Seq16 fseq_to);
+
+  DataMessage* Find(sna16_t fseq);
+  uint64_t Erase(uint16_t chksize, sna16_t fseq_to);
   uint64_t Size() const { return queue_.size(); }
   void Clear();
   DataMessage* FirstUnsent();
-  void MarkUnsentFrom(Seq16 fseq);
+  void MarkUnsentFrom(sna16_t fseq);
  private:
   std::deque<DataMessage*> queue_;
 };
@@ -159,16 +98,16 @@ class TipcPortId {
 
   struct sndwnd {
     // sender sequence window
-    Seq16 acked_{0};  // last sequence has been acked by receiver
-    Seq16 send_{1};   // next sequence to be sent
+    sna16_t acked_{0};  // last sequence has been acked by receiver
+    sna16_t send_{1};   // next sequence to be sent
     uint64_t nacked_space_{0};  // total bytes are sent but not acked
   };
   struct sndwnd sndwnd_;
 
   struct rcvwnd {
     // receiver sequence window
-    Seq16 acked_{0};  // last sequence has been acked to sender
-    Seq16 rcv_{0};    // last sequence has been received
+    sna16_t acked_{0};  // last sequence has been acked to sender
+    sna16_t rcv_{0};    // last sequence has been received
     uint64_t nacked_space_{0};  // total bytes has not been acked
   };
   struct rcvwnd rcvwnd_;
-- 
2.17.1



_______________________________________________
Opensaf-devel mailing list
Opensaf-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/opensaf-devel

Reply via email to