sort out various MTU options
authorCraig Dowell <craigdo@ee.washington.edu>
Mon, 04 Aug 2008 17:32:32 -0700
changeset 3506 cba7b2b80fe8
parent 3504 0261c789e17e
child 3507 11bfc339df59
sort out various MTU options
examples/udp-echo.cc
src/devices/csma/csma-net-device.cc
src/devices/csma/csma-net-device.h
--- a/examples/udp-echo.cc	Sun Aug 03 21:55:49 2008 -0700
+++ b/examples/udp-echo.cc	Mon Aug 04 17:32:32 2008 -0700
@@ -89,6 +89,7 @@
   CsmaHelper csma;
   csma.SetChannelAttribute ("DataRate", DataRateValue (DataRate(5000000)));
   csma.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (2)));
+  csma.SetDeviceAttribute ("MTU", UintegerValue (1400));
   NetDeviceContainer d = csma.Install (n);
 
   Ipv4AddressHelper ipv4;
--- a/src/devices/csma/csma-net-device.cc	Sun Aug 03 21:55:49 2008 -0700
+++ b/src/devices/csma/csma-net-device.cc	Mon Aug 04 17:32:32 2008 -0700
@@ -52,18 +52,20 @@
                    MakeMac48AddressChecker ())
     .AddAttribute ("PayloadLength", 
                    "The max PHY-level payload length of packets sent over this device.",
-                   UintegerValue (DEFAULT_FRAME_LENGTH),
-                   MakeUintegerAccessor (&CsmaNetDevice::m_maxPayloadLength),
+                   UintegerValue (DEFAULT_PAYLOAD_LENGTH),
+                   MakeUintegerAccessor (&CsmaNetDevice::SetMaxPayloadLength,
+                                         &CsmaNetDevice::GetMaxPayloadLength),
                    MakeUintegerChecker<uint16_t> ())
     .AddAttribute ("MTU", 
                    "The MAC-level MTU (client payload) of packets sent over this device.",
                    UintegerValue (DEFAULT_MTU),
-                   MakeUintegerAccessor (&CsmaNetDevice::m_mtu),
+                   MakeUintegerAccessor (&CsmaNetDevice::SetMacMtu,
+                                         &CsmaNetDevice::GetMacMtu),
                    MakeUintegerChecker<uint16_t> ())
     .AddAttribute ("EncapsulationMode", 
                    "The link-layer encapsulation type to use.",
                    EnumValue (LLC),
-                   MakeEnumAccessor (&CsmaNetDevice::m_encapMode),
+                   MakeEnumAccessor (&CsmaNetDevice::SetEncapsulationMode),
                    MakeEnumChecker (ETHERNET_V1, "EthernetV1",
                                     IP_ARP, "IpArp",
                                     RAW, "Raw",
@@ -106,6 +108,14 @@
   m_txMachineState = READY;
   m_tInterframeGap = Seconds (0);
   m_channel = 0; 
+
+  m_encapMode = LLC;
+  m_maxPayloadLength = DEFAULT_PAYLOAD_LENGTH;
+  m_mtu = MacMtuFromPayload (m_maxPayloadLength);
+
+  NS_LOG_LOGIC ("m_encapMode = " << m_encapMode);
+  NS_LOG_LOGIC ("m_maxPayloadLength = " << m_maxPayloadLength);
+  NS_LOG_LOGIC ("m_mtu = " << m_mtu);
 }
 
 CsmaNetDevice::~CsmaNetDevice()
@@ -123,6 +133,133 @@
   NetDevice::DoDispose ();
 }
 
+  uint16_t
+CsmaNetDevice::MacMtuFromPayload (uint16_t payloadLength)
+{
+  NS_LOG_FUNCTION (payloadLength);
+
+  switch (m_encapMode) 
+    {
+    case RAW:
+    case IP_ARP:
+    case ETHERNET_V1:
+      return payloadLength;
+    case LLC: 
+      {
+        LlcSnapHeader llc;
+
+        NS_ASSERT_MSG (payloadLength >= llc.GetSerializedSize (), "CsmaNetDevice::MacMtuFromPayload(): "
+                       "Given payload too small to support LLC mode");
+        return payloadLength - llc.GetSerializedSize ();
+      }
+    }
+
+  NS_ASSERT_MSG (false, "CsmaNetDevice::MacMtuFromPayload(): Unexpected encapsulation mode");
+  return 0;
+}
+  
+  uint16_t
+CsmaNetDevice::PayloadFromMacMtu (uint16_t mtu)
+{
+  NS_LOG_FUNCTION (mtu);
+
+  switch (m_encapMode) 
+    {
+    case RAW:
+    case IP_ARP:
+    case ETHERNET_V1:
+      return mtu;
+    case LLC: 
+      {
+        LlcSnapHeader llc;
+        return mtu + llc.GetSerializedSize ();
+      }
+    }
+
+  NS_ASSERT_MSG (false, "CsmaNetDevice::PayloadFromMacMtu(): Unexpected encapsulation mode");
+  return 0;
+}
+
+  void 
+CsmaNetDevice::SetEncapsulationMode (enum EncapsulationMode mode)
+{
+  NS_LOG_FUNCTION (mode);
+
+  m_encapMode = mode;
+  m_mtu = MacMtuFromPayload (m_maxPayloadLength);
+
+  NS_LOG_LOGIC ("m_encapMode = " << m_encapMode);
+  NS_LOG_LOGIC ("m_maxPayloadLength = " << m_maxPayloadLength);
+  NS_LOG_LOGIC ("m_mtu = " << m_mtu);
+}
+
+  CsmaNetDevice::EncapsulationMode
+CsmaNetDevice::GetEncapsulationMode (void)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return m_encapMode;
+}
+  
+  void 
+CsmaNetDevice::SetMacMtu (uint16_t mtu)
+{
+  NS_LOG_FUNCTION (mtu);
+
+  m_maxPayloadLength = PayloadFromMacMtu (mtu);
+  m_mtu = mtu;
+
+  NS_LOG_LOGIC ("m_encapMode = " << m_encapMode);
+  NS_LOG_LOGIC ("m_maxPayloadLength = " << m_maxPayloadLength);
+  NS_LOG_LOGIC ("m_mtu = " << m_mtu);
+}
+
+  uint16_t
+CsmaNetDevice::GetMacMtu (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return m_mtu;
+}
+
+//
+// The SetMtu method in the net device base class always refers to the MAC-level MTU by definition.
+//
+  bool 
+CsmaNetDevice::SetMtu (const uint16_t mtu)
+{
+  NS_LOG_FUNCTION (mtu);
+  SetMacMtu (mtu);
+  return true;
+}
+
+//
+// The GetMtu method in the net device base class always refers to the MAC-level MTU by definition.
+//
+  uint16_t 
+CsmaNetDevice::GetMtu (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return GetMacMtu ();
+}
+
+  void 
+CsmaNetDevice::SetMaxPayloadLength (uint16_t maxPayloadLength)
+{
+  NS_LOG_FUNCTION (maxPayloadLength);
+
+  m_maxPayloadLength = maxPayloadLength;
+  m_mtu = MacMtuFromPayload (maxPayloadLength);
+
+  NS_LOG_LOGIC ("m_encapMode = " << m_encapMode);
+  NS_LOG_LOGIC ("m_maxPayloadLength = " << m_maxPayloadLength);
+  NS_LOG_LOGIC ("m_mtu = " << m_mtu);
+}
+
+  uint16_t
+CsmaNetDevice::GetMaxPayloadLength (void) const
+{
+  return m_maxPayloadLength;
+}
+
   void 
 CsmaNetDevice::SetAddress (Mac48Address self)
 {
@@ -193,6 +330,7 @@
   EthernetTrailer trailer;
 
   NS_LOG_LOGIC ("p->GetSize () = " << p->GetSize ());
+  NS_LOG_LOGIC ("m_encapMode = " << m_encapMode);
   NS_LOG_LOGIC ("m_mtu = " << m_mtu);
   NS_LOG_LOGIC ("m_maxPayloadLength = " << m_maxPayloadLength);
 
@@ -238,8 +376,7 @@
       }
       break;
     case RAW:
-      NS_LOG_LOGIC ("Encapsulating packet as RAW");
-      NS_ASSERT (false);
+      NS_ASSERT_MSG (false, "CsmaNetDevice::AddHeader(): RAW packet encapsulation not supported");
       break;
     }
 
@@ -680,21 +817,6 @@
 }
 
   bool 
-CsmaNetDevice::SetMtu (const uint16_t mtu)
-{
-  NS_LOG_FUNCTION (mtu);
-  m_mtu = mtu;
-  return true;
-}
-
-  uint16_t 
-CsmaNetDevice::GetMtu (void) const
-{
-  NS_LOG_FUNCTION_NOARGS ();
-  return m_mtu;
-}
-
-  bool 
 CsmaNetDevice::IsLinkUp (void) const
 {
   NS_LOG_FUNCTION_NOARGS ();
--- a/src/devices/csma/csma-net-device.h	Sun Aug 03 21:55:49 2008 -0700
+++ b/src/devices/csma/csma-net-device.h	Mon Aug 04 17:32:32 2008 -0700
@@ -65,11 +65,12 @@
 {
 public:
   static TypeId GetTypeId (void);
+
   /**
    * Enumeration of the types of packets supported in the class.
    *
    */
-  enum CsmaEncapsulationMode {
+  enum EncapsulationMode {
     ETHERNET_V1, /**< Version one ethernet packet, length field */
     IP_ARP,      /**< Ethernet packet encapsulates IP/ARP packet */
     RAW,         /**< Packet that contains no headers */
@@ -197,17 +198,108 @@
    */
   void SetAddress (Mac48Address addr);
 
-//
-// The following methods are inherited from NetDevice base class.
-//
+  /**
+   * Set The max PHY-level payload length of packets sent over this device.
+   *
+   * Okay, that was easy to say, but the details are a bit thorny.  We have a MAC-level that is the payload that higher 
+   * level protocols see.  We have a PHY-level MTU which is the maximum number of bytes we can send over the link 
+   * (cf. 1500 bytes for Ethernet).  The value that determines the relationship between these two values is the link
+   * encapsulation mode.  The link encapsulation defines the number of bytes of overhead that are required for the particular
+   * MAC protocol used.  For example, if the LLC/SNAP encapsulation is used, eight bytes of LLC/SNAP header are consumed and
+   * therefore the MAC-level MTU must be set and reported as eight bytes less than the PHY-level MTU (which we call the
+   * payload length to try and avoid confusion).
+   *
+   * So, what do we do since there must be three values which must always be consistent in the driver?  Which values to we
+   * allow to be changed and how do we ensure the other two are consistent?  We want to actually enable a user to change 
+   * these two payload lengths in flexible ways, but we want the results (even at intermediate stages) to be consistent.  
+   * We certainly don't want to require that users must understand the various requirements of an enapsulation mode in order 
+   * to set these variables.
+   *
+   * Consider the following situation:  A user wants to set the physical layer MTU to 1400 bytes instead of 1500.  This
+   * user shouldn't have to concern herself that the current encapuslation mode is LLC and this will consume eight bytes.
+   * She should not have to also set the MAC MTU to 1392 bytes, and she should certainly not have to do this before setting
+   * the PHY MTU.  
+   *
+   * A user who is interested in setting the MAC-level MTU to 1400 bytes should not be forced to understand that in certain
+   * cases the PHY-level MTU must be set to eight bytes more than what he wants in certain cases and zero bytes in others.
+   *
+   * Now, consider a user who is only interested in changing the encapsulation mode from LLC/SNAP to ETHERNET_V1.  This 
+   * is going to change the relationship between the MAC MTU and the PHY MTU.  We've may have to come up with a new value 
+   * for at least one of the MTUs?  Which one?
+   *
+   * We could play games trying to figure out what the user wants to do, but that is typically a bad plan.  So we're going
+   * to just define a flexible behavior.  Here it is:
+   *
+   * - If the user is changing the encapsulation mode, the PHY MTU will remain fixed and the MAC MTU will change, if required,
+   * to make the three values consistent;
+   *
+   * - If the user is changing the MAC MTU, she is interested in getting that part of the system set, so the PHY MTU
+   * will be changed to make the three values consistent;
+   *
+   * - If the user is changing the PHY MTU, he is interested in getting that part of the system set, so the MAC MTU
+   * will be changed to make the three values consistent.
+   * 
+   * So, if a user calls SetMaxPayloadLength, we assume that the PHY-level MTU is the interesting thing for that user and
+   * we just adjust the MAC-level MTU to "the correct value" based on the current encapsulation mode.  If a user calls 
+   * SetMacMtu, we assume that the MAC-level MTU is the interesting property for that user, and we adjust the PHY-level MTU 
+   * to "the correct value" for the current encapsulation mode.  If a user calls SetEncapsulationMode, then we take the
+   * MAC-level MTU as the free variable and set its value to match the current PHY-level MTU.
+   *
+   * \param mayPayloadLength The max PHY-level payload length of packets sent over this device.
+   */
+  void SetMaxPayloadLength (uint16_t maxPayloadLength);
+
+  /**
+   * Get The max PHY-level payload length of packets sent over this device.
+   *
+   * \returns The max PHY-level payload length of packets sent over this device.
+   */
+  uint16_t GetMaxPayloadLength (void) const;
+
+  /**
+   * Set The MAC-level MTU (client payload) of packets sent over this device.
+   *
+   * \param mtu The MAC-level MTU (client payload) of packets sent over this device.
+   *
+   * \see SetMaxPayloadLength
+   */
+  void SetMacMtu (uint16_t mtu);
+
+  /**
+   * Get The MAC-level MTU (client payload) of packets sent over this device.
+   *
+   * \returns The MAC-level MTU (client payload) of packets sent over this device.
+   */
+  uint16_t GetMacMtu (void) const;
+
+
+  /**
+   * Set the encapsulation mode of this device.
+   *
+   * \param mode The encapsulation mode of this device.
+   *
+   * \see SetMaxPayloadLength
+   */
+  void SetEncapsulationMode (CsmaNetDevice::EncapsulationMode mode);
+
+  /**
+   * Get the encapsulation mode of this device.
+   *
+   * \returns The encapsulation mode of this device.
+   */
+  CsmaNetDevice::EncapsulationMode  GetEncapsulationMode (void);
+
+  //
+  // The following methods are inherited from NetDevice base class.
+  //
   virtual void SetName (const std::string name);
   virtual std::string GetName (void) const;
   virtual void SetIfIndex (const uint32_t index);
   virtual uint32_t GetIfIndex (void) const;
   virtual Ptr<Channel> GetChannel (void) const;
-  virtual Address GetAddress (void) const;
   virtual bool SetMtu (const uint16_t mtu);
   virtual uint16_t GetMtu (void) const;
+  virtual Address GetAddress (void) const;
   virtual bool IsLinkUp (void) const;
   virtual void SetLinkChangeCallback (Callback<void> callback);
   virtual bool IsBroadcast (void) const;
@@ -333,9 +425,6 @@
 
 private:
 
-  static const uint16_t DEFAULT_FRAME_LENGTH = 1500;
-  static const uint16_t DEFAULT_MTU = 1492;
-
   /**
    * Operator = is declared but not implemented.  This disables the assigment
    * operator for CsmaNetDevice objects.
@@ -355,6 +444,18 @@
   void Init (bool sendEnable, bool receiveEnable);
 
   /**
+   * Calculate the value for the MAC-level MTU that would result from 
+   * setting the PHY-level MTU to the given value.
+   */
+  uint16_t MacMtuFromPayload (uint16_t payloadLength);
+
+  /**
+   * Calculate the value for the PHY-level MTU that would be required
+   * to be able to set the MAC-level MTU to the given value.
+   */
+  uint16_t PayloadFromMacMtu (uint16_t mtu);
+
+  /**
    * Start Sending a Packet Down the Wire.
    *
    * The TransmitStart method is the method that is used internally in
@@ -460,7 +561,7 @@
    * function and that should be processed by the ProcessHeader
    * function.
    */
-  CsmaEncapsulationMode m_encapMode;
+  EncapsulationMode m_encapMode;
 
   /**
    * The data rate that the Net Device uses to simulate packet transmission
@@ -568,6 +669,28 @@
    */
   Callback<void> m_linkChangeCallback;
 
+  static const uint16_t DEFAULT_PAYLOAD_LENGTH = 1500;
+  static const uint16_t DEFAULT_MTU = 1492;
+
+  /**
+   * There are two MTU types that are used in this driver.  The MAC-level 
+   * MTU corresponds to the amount of data (payload) an upper layer can 
+   * send across the link.  The PHY-level MTU corresponds to the Type/Length
+   * field in the 802.3 header and corresponds to the maximum amount of data
+   * the underlying packet can accept.  These are not the same thing.  For 
+   * example, if you choose "Llc" as your encapsulation mode, the MAC-level
+   * MTU will be reduced by the eight bytes with respect to the PHY-level
+   * MTU which are consumed by the LLC/SNAP header.
+   *
+   * This method checks the current enacpuslation mode (and any other 
+   * relevent information) and determines if the provided payloadLength 
+   * (PHY-level MTU) and mtu (MAC-level MTU) are consistent.
+   *
+   * \param payloadLength The proposed PHY-level MTU
+   * \param mtu The proposed MAC-level MTU
+   */
+  bool CheckMtuConsistency (uint16_t payloadLength, uint16_t mtu);
+
   /**
    * The MAC-level maximum transmission unit allowed to be sent or received by
    * this network device.  This corresponds to the maximum payload the device