cleanup ip alias unfinished cases (bug 591)
authorTom Henderson <tomh@tomh.org>
Sun, 21 Jun 2009 22:29:08 -0700
changeset 4571 2c9c600270e0
parent 4570 5c6e1f086a36
child 4572 e5dcdf2868df
cleanup ip alias unfinished cases (bug 591)
examples/csma-broadcast.cc
src/internet-stack/arp-l3-protocol.cc
src/internet-stack/icmpv4-l4-protocol.cc
src/internet-stack/icmpv4-l4-protocol.h
src/internet-stack/ipv4-end-point-demux.cc
src/internet-stack/ipv4-l3-protocol.cc
src/internet-stack/ipv4-l3-protocol.h
src/internet-stack/ipv4-static-routing-impl.cc
src/internet-stack/ipv4-static-routing-impl.h
src/internet-stack/ipv4-test.cc
src/internet-stack/wscript
--- a/examples/csma-broadcast.cc	Fri Jun 19 14:12:59 2009 -0700
+++ b/examples/csma-broadcast.cc	Sun Jun 21 22:29:08 2009 -0700
@@ -102,8 +102,10 @@
   // Create an optional packet sink to receive these packets
   PacketSinkHelper sink ("ns3::UdpSocketFactory",
     Address (InetSocketAddress (Ipv4Address::GetAny (), port)));
-  sink.Install (c0.Get (1));
-  sink.Install (c1.Get (1));
+  app = sink.Install (c0.Get (1));
+  app.Add (sink.Install (c1.Get (1)));
+  app.Start (Seconds (1.0));
+  app.Stop (Seconds (10.0));
 
   // Also configure some tcpdump traces; each interface will be traced
   // The output files will be named 
--- a/src/internet-stack/arp-l3-protocol.cc	Fri Jun 19 14:12:59 2009 -0700
+++ b/src/internet-stack/arp-l3-protocol.cc	Sun Jun 21 22:29:08 2009 -0700
@@ -23,6 +23,7 @@
 #include "ns3/net-device.h"
 #include "ns3/object-vector.h"
 #include "ns3/trace-source-accessor.h"
+#include "ns3/ipv4-route.h"
 
 #include "ipv4-l3-protocol.h"
 #include "arp-l3-protocol.h"
@@ -160,73 +161,82 @@
       NS_LOG_LOGIC ("ARP: Cannot remove ARP header");
       return;
     }
-  // XXX multi-address case  
   NS_LOG_LOGIC ("ARP: received "<< (arp.IsRequest ()? "request" : "reply") <<
             " node="<<m_node->GetId ()<<", got request from " <<
             arp.GetSourceIpv4Address () << " for address " <<
-            arp.GetDestinationIpv4Address () << "; we have address " <<
-            cache->GetInterface ()->GetAddress (0).GetLocal ());
+            arp.GetDestinationIpv4Address () << "; we have addresses: ");
+  for (uint32_t i = 0; i < cache->GetInterface ()->GetNAddresses (); i++)
+    {
+      NS_LOG_LOGIC (cache->GetInterface ()->GetAddress (i).GetLocal () << ", ");
+    }
 
   /**
    * Note: we do not update the ARP cache when we receive an ARP request
    * from an unknown node. See bug #107
    */
-  // XXX multi-address case
-  if (arp.IsRequest () && 
-      arp.GetDestinationIpv4Address () == cache->GetInterface ()->GetAddress (0).GetLocal ()) 
-    {
-      NS_LOG_LOGIC ("node="<<m_node->GetId () <<", got request from " << 
-                arp.GetSourceIpv4Address () << " -- send reply");
-      SendArpReply (cache, arp.GetSourceIpv4Address (),
-                    arp.GetSourceHardwareAddress ());
-    } 
-  // XXX multi-address case
-  else if (arp.IsReply () &&
-           arp.GetDestinationIpv4Address ().IsEqual (cache->GetInterface ()->GetAddress (0).GetLocal ()) &&
-           arp.GetDestinationHardwareAddress () == device->GetAddress ()) 
+  bool found = false;
+  for (uint32_t i = 0; i < cache->GetInterface ()->GetNAddresses (); i++)
     {
-      Ipv4Address from = arp.GetSourceIpv4Address ();
-      ArpCache::Entry *entry = cache->Lookup (from);
-      if (entry != 0)
+      if (arp.IsRequest () && arp.GetDestinationIpv4Address () == 
+        cache->GetInterface ()->GetAddress (i).GetLocal ()) 
         {
-          if (entry->IsWaitReply ()) 
+          found = true;
+          NS_LOG_LOGIC ("node="<<m_node->GetId () <<", got request from " << 
+                arp.GetSourceIpv4Address () << " -- send reply");
+          SendArpReply (cache, arp.GetSourceIpv4Address (),
+                    arp.GetSourceHardwareAddress ());
+          break;
+        } 
+      else if (arp.IsReply () && 
+        arp.GetDestinationIpv4Address ().IsEqual (cache->GetInterface ()->GetAddress (i).GetLocal ()) &&
+        arp.GetDestinationHardwareAddress () == device->GetAddress ()) 
+        {
+          found = true;
+          Ipv4Address from = arp.GetSourceIpv4Address ();
+          ArpCache::Entry *entry = cache->Lookup (from);
+          if (entry != 0)
             {
-              NS_LOG_LOGIC ("node="<<m_node->GetId ()<<", got reply from " << 
-                        arp.GetSourceIpv4Address ()
+              if (entry->IsWaitReply ()) 
+                {
+                  NS_LOG_LOGIC ("node="<< m_node->GetId () << 
+                    ", got reply from " << arp.GetSourceIpv4Address ()
                      << " for waiting entry -- flush");
-              Address from_mac = arp.GetSourceHardwareAddress ();
-              entry->MarkAlive (from_mac);
-              Ptr<Packet> pending = entry->DequeuePending();
-              while (pending != 0)
+                  Address from_mac = arp.GetSourceHardwareAddress ();
+                  entry->MarkAlive (from_mac);
+                  Ptr<Packet> pending = entry->DequeuePending();
+                  while (pending != 0)
+                    {
+                      cache->GetInterface ()->Send (pending,
+                                                arp.GetSourceIpv4Address ());
+                      pending = entry->DequeuePending();
+                    }
+                } 
+              else 
                 {
-                  cache->GetInterface ()->Send (pending,
-                                                arp.GetSourceIpv4Address ());
-                  pending = entry->DequeuePending();
+                  // ignore this reply which might well be an attempt 
+                  // at poisening my arp cache.
+                  NS_LOG_LOGIC("node="<<m_node->GetId ()<<", got reply from " << 
+                        arp.GetSourceIpv4Address () << 
+                        " for non-waiting entry -- drop");
+                  m_dropTrace (packet);
                 }
             } 
           else 
             {
-              // ignore this reply which might well be an attempt 
-              // at poisening my arp cache.
-              NS_LOG_LOGIC("node="<<m_node->GetId ()<<", got reply from " << 
-                        arp.GetSourceIpv4Address () << 
-                        " for non-waiting entry -- drop");
+              NS_LOG_LOGIC ("node="<<m_node->GetId ()<<", got reply for unknown entry -- drop");
               m_dropTrace (packet);
             }
-        } 
-      else 
-        {
-          NS_LOG_LOGIC ("node="<<m_node->GetId ()<<", got reply for unknown entry -- drop");
-          m_dropTrace (packet);
+          break;
         }
     }
-  else
+  if (found == false)
     {
       NS_LOG_LOGIC ("node="<<m_node->GetId ()<<", got request from " <<
                 arp.GetSourceIpv4Address () << " for unknown address " <<
                 arp.GetDestinationIpv4Address () << " -- drop");
     }
 }
+
 bool 
 ArpL3Protocol::Lookup (Ptr<Packet> packet, Ipv4Address destination, 
                        Ptr<NetDevice> device,
@@ -301,14 +311,22 @@
 {
   NS_LOG_FUNCTION (this << cache << to);
   ArpHeader arp;
+  // need to pick a source address; use routing implementation to select
+  Ptr<Ipv4L3Protocol> ipv4 = m_node->GetObject<Ipv4L3Protocol> ();
+  int32_t interface = ipv4->GetInterfaceForDevice (cache->GetDevice ());
+  NS_ASSERT (interface >= 0);
+  Ipv4Header header;
+  header.SetDestination (to);
+  Socket::SocketErrno errno_;
+  Ptr<Ipv4Route> route = ipv4->GetRoutingProtocol ()->RouteOutput (header, interface, errno_);
+  NS_ASSERT (route != 0);
   NS_LOG_LOGIC ("ARP: sending request from node "<<m_node->GetId ()<<
             " || src: " << cache->GetDevice ()->GetAddress () <<
-            " / " << cache->GetInterface ()->GetAddress (0).GetLocal () <<
+            " / " << route->GetSource () <<
             " || dst: " << cache->GetDevice ()->GetBroadcast () <<
             " / " << to);
-  // XXX multi-address case
   arp.SetRequest (cache->GetDevice ()->GetAddress (),
-		  cache->GetInterface ()->GetAddress (0).GetLocal (), 
+		  route->GetSource (),
                   cache->GetDevice ()->GetBroadcast (),
                   to);
   Ptr<Packet> packet = Create<Packet> ();
@@ -321,14 +339,21 @@
 {
   NS_LOG_FUNCTION (this << cache << toIp << toMac);
   ArpHeader arp;
+  // need to pick a source address; use routing implementation to select
+  Ptr<Ipv4L3Protocol> ipv4 = m_node->GetObject<Ipv4L3Protocol> ();
+  int32_t interface = ipv4->GetInterfaceForDevice (cache->GetDevice ());
+  NS_ASSERT (interface >= 0);
+  Ipv4Header header;
+  header.SetDestination (toIp);
+  Socket::SocketErrno errno_;
+  Ptr<Ipv4Route> route = ipv4->GetRoutingProtocol ()->RouteOutput (header, interface, errno_);
+  NS_ASSERT (route != 0);
   NS_LOG_LOGIC ("ARP: sending reply from node "<<m_node->GetId ()<<
             "|| src: " << cache->GetDevice ()->GetAddress () << 
-            " / " << cache->GetInterface ()->GetAddress (0).GetLocal () <<
+            " / " << route->GetSource () <<
             " || dst: " << toMac << " / " << toIp);
-  // XXX multi-address case
   arp.SetReply (cache->GetDevice ()->GetAddress (),
-                cache->GetInterface ()->GetAddress (0).GetLocal (),
-                toMac, toIp);
+                route->GetSource (), toMac, toIp);
   Ptr<Packet> packet = Create<Packet> ();
   packet->AddHeader (arp);
   cache->GetDevice ()->Send (packet, toMac, PROT_NUMBER);
--- a/src/internet-stack/icmpv4-l4-protocol.cc	Fri Jun 19 14:12:59 2009 -0700
+++ b/src/internet-stack/icmpv4-l4-protocol.cc	Sun Jun 21 22:29:08 2009 -0700
@@ -16,9 +16,7 @@
 NS_OBJECT_ENSURE_REGISTERED (Icmpv4L4Protocol);
 
   // see rfc 792
-enum {
- ICMP_PROTOCOL = 1
-};
+const uint8_t Icmpv4L4Protocol::PROT_NUMBER = 1;
 
 TypeId 
 Icmpv4L4Protocol::GetTypeId (void)
@@ -68,13 +66,13 @@
 uint16_t 
 Icmpv4L4Protocol::GetStaticProtocolNumber (void)
 {
-  return ICMP_PROTOCOL;
+  return PROT_NUMBER;
 }
 
 int 
 Icmpv4L4Protocol::GetProtocolNumber (void) const
 {
-  return ICMP_PROTOCOL;
+  return PROT_NUMBER;
 }
 void
 Icmpv4L4Protocol::SendMessage (Ptr<Packet> packet, Ipv4Address dest, uint8_t type, uint8_t code)
@@ -111,7 +109,7 @@
       icmp.EnableChecksum ();
     }
   packet->AddHeader (icmp);
-  ipv4->Send (packet, source, dest, ICMP_PROTOCOL, route);
+  ipv4->Send (packet, source, dest, PROT_NUMBER, route);
 }
 void 
 Icmpv4L4Protocol::SendDestUnreachFragNeeded (Ipv4Header header, 
--- a/src/internet-stack/icmpv4-l4-protocol.h	Fri Jun 19 14:12:59 2009 -0700
+++ b/src/internet-stack/icmpv4-l4-protocol.h	Sun Jun 21 22:29:08 2009 -0700
@@ -15,6 +15,8 @@
 {
 public:
   static TypeId GetTypeId (void);
+  static const uint8_t PROT_NUMBER;
+
   Icmpv4L4Protocol ();
   virtual ~Icmpv4L4Protocol ();
 
--- a/src/internet-stack/ipv4-end-point-demux.cc	Fri Jun 19 14:12:59 2009 -0700
+++ b/src/internet-stack/ipv4-end-point-demux.cc	Sun Jun 21 22:29:08 2009 -0700
@@ -219,11 +219,19 @@
                         << " does not match packet dport " << dport);
           continue;
         }
-      // XXX handle multi-address case
-      bool isBroadcast = (daddr.IsBroadcast () ||
-         daddr.IsSubnetDirectedBroadcast (
-             incomingInterface->GetAddress (0).GetMask ()));
-      Ipv4Address incomingInterfaceAddr = incomingInterface->GetAddress (0).GetLocal ();
+      bool subnetDirected = false;
+      Ipv4Address incomingInterfaceAddr = daddr;  // may be a broadcast
+      for (uint32_t i = 0; i < incomingInterface->GetNAddresses (); i++)
+        {
+          Ipv4InterfaceAddress addr = incomingInterface->GetAddress (i);
+          if (addr.GetLocal ().CombineMask (addr.GetMask ()) == daddr.CombineMask (addr.GetMask ()) &&
+            daddr.IsSubnetDirectedBroadcast (addr.GetMask ()))
+            {
+              subnetDirected = true;
+              incomingInterfaceAddr = addr.GetLocal ();
+            }
+        }
+      bool isBroadcast = (daddr.IsBroadcast () || subnetDirected == true);
       NS_LOG_DEBUG ("dest addr " << daddr << " broadcast? " << isBroadcast);
       bool localAddressMatchesWildCard = 
         endP->GetLocalAddress() == Ipv4Address::GetAny();
--- a/src/internet-stack/ipv4-l3-protocol.cc	Fri Jun 19 14:12:59 2009 -0700
+++ b/src/internet-stack/ipv4-l3-protocol.cc	Sun Jun 21 22:29:08 2009 -0700
@@ -526,8 +526,8 @@
       // This could arise because the synchronous RouteOutput() call
       // returned to the transport protocol with a source address but
       // there was no next hop available yet (since a route may need
-      // to be queried).  So, call asynchronous version of RouteOutput?
-      NS_FATAL_ERROR("XXX This case not yet implemented");
+      // to be queried).  
+      NS_FATAL_ERROR ("This case not yet implemented");
     }
   // 5) packet is not broadcast, and route is NULL (e.g., a raw socket call)
   NS_LOG_LOGIC ("Ipv4L3Protocol::Send case 4:  passed in with no route " << destination);
@@ -684,11 +684,10 @@
   ipHeader.SetTtl (ipHeader.GetTtl () - 1);
   if (ipHeader.GetTtl () == 0)
     {
-      Ptr<NetDevice> outDev = rtentry->GetOutputDevice ();
-      int32_t interface = GetInterfaceForDevice (outDev);
-      NS_ASSERT (interface >= 0);
-      // XXX No check here for possibly multiple addresses on this interface
-      if (IsUnicast (ipHeader.GetDestination (), GetInterface (interface)->GetAddress (0).GetMask ()))
+      // Do not reply to ICMP or to multicast/broadcast IP address 
+      if (ipHeader.GetProtocol () != Icmpv4L4Protocol::PROT_NUMBER && 
+        ipHeader.GetDestination ().IsBroadcast () == false && 
+        ipHeader.GetDestination ().IsMulticast () == false)
         {
           Ptr<Icmpv4L4Protocol> icmp = GetIcmp ();
           icmp->SendTimeExceededTtl (ipHeader, packet);
@@ -720,12 +719,26 @@
       case Ipv4L4Protocol::RX_CSUM_FAILED:
         break;
       case Ipv4L4Protocol::RX_ENDPOINT_UNREACH:
-        // XXX handle possibly multiple IP addresses on the interface
-        if (IsUnicast (ip.GetDestination (), GetInterface (iif)->GetAddress (0).GetMask ()))
+        if (ip.GetDestination ().IsBroadcast () == true || 
+          ip.GetDestination ().IsMulticast () == true)
+          {
+            break;  // Do not reply to broadcast or multicast
+          }
+        // Another case to suppress ICMP is a subnet-directed broadcast
+        bool subnetDirected = false;
+        for (uint32_t i = 0; i < GetNAddresses (iif); i++)
+          {
+            Ipv4InterfaceAddress addr = GetAddress (iif, i);
+            if (addr.GetLocal ().CombineMask (addr.GetMask ()) == ip.GetDestination().CombineMask (addr.GetMask ()) &&
+              ip.GetDestination ().IsSubnetDirectedBroadcast (addr.GetMask ()))
+              {
+                subnetDirected = true;
+              }
+          }
+        if (subnetDirected == false)
           {
             GetIcmp ()->SendDestUnreachPort (ip, copy);
           }
-        break;
       }
     }
 }
--- a/src/internet-stack/ipv4-l3-protocol.h	Fri Jun 19 14:12:59 2009 -0700
+++ b/src/internet-stack/ipv4-l3-protocol.h	Sun Jun 21 22:29:08 2009 -0700
@@ -168,6 +168,7 @@
    */
   virtual void NotifyNewAggregate ();
 private:
+  friend class Ipv4L3ProtocolTest;
   Ipv4L3Protocol(const Ipv4L3Protocol &);
   Ipv4L3Protocol &operator = (const Ipv4L3Protocol &);
 
--- a/src/internet-stack/ipv4-static-routing-impl.cc	Fri Jun 19 14:12:59 2009 -0700
+++ b/src/internet-stack/ipv4-static-routing-impl.cc	Sun Jun 21 22:29:08 2009 -0700
@@ -224,11 +224,10 @@
           NS_LOG_LOGIC ("Found global host route" << *i);
           Ipv4RoutingTableEntry* route = (*i);
           rtentry = Create<Ipv4Route> ();
+          uint32_t interfaceIdx = route->GetInterface ();
           rtentry->SetDestination (route->GetDest ());
-          // XXX handle multi-address case
-          rtentry->SetSource (m_ipv4->GetAddress (route->GetInterface(), 0).GetLocal ());
+          rtentry->SetSource (SourceAddressSelection (interfaceIdx, route->GetDest ()));
           rtentry->SetGateway (route->GetGateway ());
-          uint32_t interfaceIdx = route->GetInterface ();
           rtentry->SetOutputDevice (m_ipv4->GetNetDevice (interfaceIdx));
           return rtentry;
         }
@@ -245,11 +244,10 @@
           NS_LOG_LOGIC ("Found global network route" << *j);
           Ipv4RoutingTableEntry* route = (*j);
           rtentry = Create<Ipv4Route> ();
+          uint32_t interfaceIdx = route->GetInterface ();
           rtentry->SetDestination (route->GetDest ());
-          // XXX handle multi-address case
-          rtentry->SetSource (m_ipv4->GetAddress (route->GetInterface(), 0).GetLocal ());
+          rtentry->SetSource (SourceAddressSelection (interfaceIdx, route->GetDest ()));
           rtentry->SetGateway (route->GetGateway ());
-          uint32_t interfaceIdx = route->GetInterface ();
           rtentry->SetOutputDevice (m_ipv4->GetNetDevice (interfaceIdx));
           return rtentry;
         }
@@ -260,11 +258,10 @@
       NS_LOG_LOGIC ("Found global network route" << m_defaultRoute);
       Ipv4RoutingTableEntry* route = m_defaultRoute;
       rtentry = Create<Ipv4Route> ();
+      uint32_t interfaceIdx = route->GetInterface ();
       rtentry->SetDestination (route->GetDest ());
-      // XXX handle multi-address case
-      rtentry->SetSource (m_ipv4->GetAddress (route->GetInterface(), 0).GetLocal ());
+      rtentry->SetSource (SourceAddressSelection (interfaceIdx, route->GetDest ()));
       rtentry->SetGateway (route->GetGateway ());
-      uint32_t interfaceIdx = route->GetInterface ();
       rtentry->SetOutputDevice (m_ipv4->GetNetDevice (interfaceIdx));
       return rtentry;
     }
@@ -469,7 +466,8 @@
   return rtentry;
 }
 
-// XXX this method not robust enough to work independent of ListRouting
+// XXX this method not robust enough to work outside of ListRouting context
+// because it will not perform local delivery
 bool 
 Ipv4StaticRoutingImpl::RouteInput  (Ptr<const Packet> p, const Ipv4Header &ipHeader, Ptr<const NetDevice> idev,
                              UnicastForwardCallback ucb, MulticastForwardCallback mcb,
@@ -641,4 +639,30 @@
     }
 }
 
+Ipv4Address
+Ipv4StaticRoutingImpl::SourceAddressSelection (uint32_t interfaceIdx, Ipv4Address dest)
+{
+  if (m_ipv4->GetNAddresses (interfaceIdx) == 1)  // common case
+    {
+      return m_ipv4->GetAddress (interfaceIdx, 0).GetLocal ();
+    }
+  // no way to determine the scope of the destination, so adopt the
+  // following rule:  pick the first available address (index 0) unless
+  // a subsequent address is on link (in which case, pick the primary
+  // address if there are multiple)
+  Ipv4Address candidate = m_ipv4->GetAddress (interfaceIdx, 0).GetLocal ();
+  for (uint32_t i = 0; i < m_ipv4->GetNAddresses (interfaceIdx); i++)
+    {
+      Ipv4InterfaceAddress test = m_ipv4->GetAddress (interfaceIdx, i);
+      if (test.GetLocal ().CombineMask (test.GetMask ()) == dest.CombineMask (test.GetMask ()))
+        {
+          if (test.IsSecondary () == false) 
+            {
+              return test.GetLocal ();
+            }
+        }
+    }
+  return candidate;
+}
+
 }//namespace ns3
--- a/src/internet-stack/ipv4-static-routing-impl.h	Fri Jun 19 14:12:59 2009 -0700
+++ b/src/internet-stack/ipv4-static-routing-impl.h	Sun Jun 21 22:29:08 2009 -0700
@@ -358,6 +358,8 @@
   Ptr<Ipv4MulticastRoute> LookupStatic (Ipv4Address origin, Ipv4Address group,
                                     uint32_t interface);
 
+  Ipv4Address SourceAddressSelection (uint32_t interface, Ipv4Address dest);
+
   HostRoutes m_hostRoutes;
   NetworkRoutes m_networkRoutes;
   Ipv4RoutingTableEntry *m_defaultRoute;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/internet-stack/ipv4-test.cc	Sun Jun 21 22:29:08 2009 -0700
@@ -0,0 +1,87 @@
+/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+/**
+ * This is the test code for ipv4-l3-protocol.cc  
+ */
+#ifdef RUN_SELF_TESTS
+
+#include "ns3/simulator.h"
+#include "ns3/test.h"
+#include "ns3/log.h"
+#include "ns3/inet-socket-address.h"
+#include "ns3/node.h"
+
+#include "ipv4-l3-protocol.h"
+#include "arp-l3-protocol.h"
+#include "ipv4-interface.h"
+#include "loopback-net-device.h"
+
+namespace ns3 {
+
+
+class Ipv4L3ProtocolTest: public Test
+{
+public:
+  virtual bool RunTests (void);
+  Ipv4L3ProtocolTest ();
+
+};
+
+
+Ipv4L3ProtocolTest::Ipv4L3ProtocolTest ()
+  : Test ("Ipv4L3Protocol") 
+{
+}
+
+bool
+Ipv4L3ProtocolTest::RunTests (void)
+{
+  bool result = true;
+  Ptr<Node> node = CreateObject<Node> ();
+  Ptr<Ipv4L3Protocol> ipv4 = CreateObject<Ipv4L3Protocol> ();
+  Ptr<Ipv4Interface> interface = CreateObject<Ipv4Interface> ();
+  Ptr<LoopbackNetDevice> device = CreateObject<LoopbackNetDevice> ();
+  node->AddDevice (device);
+  interface->SetDevice (device);
+  interface->SetNode (node);
+  uint32_t index = ipv4->AddIpv4Interface (interface);
+  NS_TEST_ASSERT_EQUAL (index, 0);
+  interface->SetUp ();
+  Ipv4InterfaceAddress ifaceAddr1 = Ipv4InterfaceAddress ("192.168.0.1", "255.255.255.0");
+  interface->AddAddress (ifaceAddr1);
+  Ipv4InterfaceAddress ifaceAddr2 = Ipv4InterfaceAddress ("192.168.0.2", "255.255.255.0");
+  interface->AddAddress (ifaceAddr2);
+  Ipv4InterfaceAddress ifaceAddr3 = Ipv4InterfaceAddress ("10.30.0.1", "255.255.255.0");
+  interface->AddAddress (ifaceAddr3);
+  Ipv4InterfaceAddress ifaceAddr4 = Ipv4InterfaceAddress ("250.0.0.1", "255.255.255.0");
+  interface->AddAddress (ifaceAddr4);
+  uint32_t num = interface->GetNAddresses ();
+  NS_TEST_ASSERT_EQUAL (num, 4);
+  interface->RemoveAddress (2);
+  num = interface->GetNAddresses ();
+  NS_TEST_ASSERT_EQUAL (num, 3);
+  Ipv4InterfaceAddress output = interface->GetAddress (2);
+  NS_TEST_ASSERT_EQUAL (ifaceAddr4, output);
+  
+  return result;
+}
+
+static Ipv4L3ProtocolTest gIpv4L3ProtocolTest;
+
+}; // namespace ns3
+
+#endif /* RUN_SELF_TESTS */
--- a/src/internet-stack/wscript	Fri Jun 19 14:12:59 2009 -0700
+++ b/src/internet-stack/wscript	Sun Jun 21 22:29:08 2009 -0700
@@ -74,6 +74,7 @@
     obj.source = [
         'tcp-test.cc',
         'udp-test.cc',
+        'ipv4-test.cc',
         'ipv4-l4-protocol.cc',
         'udp-header.cc',
         'tcp-header.cc',