[Ns-developers] [ns3] socket API
Mathieu Lacage
mathieu.lacage at sophia.inria.fr
Mon Apr 14 14:37:03 PDT 2008
hi,
Trying to implement a synchronous socket API on top of the ns3 API
turned out to be pretty useful, if only it showed some issues with the
underlying async API. Tom and I talked about some of these issues on
friday and I spent a while pondering over them and others so, here is a
summary of the parts of the socket API which are missing to implement a
full posix API on top. Some of the key issues I tried to address:
- ability to implement a fully-conformant posix API on top
- should make it possible to implement efficient versions of these
APIs. i.e., implementations which do not copy packet data from the
application to the socket layer.
- allow the posix socket API to be extended with a version of
functions which take Ptr<Packet>, to allow _really_ efficient
synchronous applications which do not copy data from application to
socket.
There is quite a bit of content below but it would be nice to get
feedback on the basic approach and, then, I can try to prototype it in
the ns-3-simu branch to validate it.
1) need to implement a "pull" API in ns3::Socket. The assumption is that
the subclass of the ns3::Socket class we want to use will implement
internally a tx and an rx buffer: the synchronous wrapper on top of it
needs to access that buffer to implement the normal recv calls.
Proposal:
/**
* return a _single_ packet by removing it from the internal rx
* buffer if it is smaller than maxSize. If the size of
* the next packet to return is bigger than maxSize, return 0 and
* set ENOMEM.
* If you want to get the "from" address, it is stored as a tag
* in the packet returned.
* _Must_ be possible to call this method from the Recv callback.
*/
Ptr<Packet> Recv (uint32_t maxSize, uint32_t flags);
/**
* Return number of bytes which can be returned from one or
* multiple calls to Recv.
* Must be possible to call this method from the Recv callback.
*/
uint32_t GetRxAvailable (void) const;
The above two methods should be enough to implement a synchronous recv
function. If we implement them, we should remove the packet and address
arguments from the RecvCallback which would then be of type
Callback<void>.
2) the current Socket::Send method is quite painful to use and
implement. Proposal:
/**
* return -1 if packet does not fit in tx buffer (current behavior is
* to send the part of the packet which fits in the tx buffer).
* If the packet does not fit, the caller can split the packet to send
* (based on output by GetTxAvailable) and re-attempt to send the
* packet.
* If you want to provide a per-packet "to" address, store it as a
* special tag in the outgoing packet.
*/
int Send (Ptr<Packet> p, uint32_t flags);
/**
* returns the number of bytes which can be sent in a single call
* to Send.
*/
uint32_t GetTxAvailable (void) const;
If we implement this, we should remove the SendTo method. Problem: if we
do this, we cannot specify the "to" address for
int Socket::SendTo (uint8_t *buffer, uint32_t size);
because there is no Ptr<Packet> to attach the "to" address to.
Solution: get rid of the Socket::Send* methods which take raw buffers
and implement them exclusively at the "synchronous" layer.
3) How should socket options and ioctls be supported ?
Proposal:
in src/node:
class Ipv4SocketOptions
{
/* Implement the linux IP_PKTINFO socket option. */
virtual void EnablePktInfo (void) = 0;
virtual void DisablePktInfo (void) = 0;
virtual bool IsPktInfoEnabled (void) = 0;
/* Implement the linux IP_OPTIONS socket option. */
virtual void SetOptions (const uint8_t [40] buffer, uint32_t size);
// return size of options data.
virtual uint32_t GetOptions (uint8_t [40] buffer);
/* implement IP_RECVTTL */
virtual void EnableRecvTtl (void) = 0;
virtual void DisableRecvTtl (void) = 0;
virtual uint32_t IsRecvTtlEnabled (void) = 0;
// all others...
};
class UdpSocketOptions : public Ipv4SocketOptions
{
public:
// udp-specific options.
};
class TcpSocketOptions : public Ipv4SocketOptions {...};
All the above would be implemented in src/internet-node/ as aggregated
classes to each Socket. i.e., user code:
Ptr<Socket> sock = ...;
Ptr<Ipv4SocketOptions> ipv4 = sock->GetObject<Ipv4SocketOptions> ();
NS_ASSERT (ipv4 != 0);
ipv4->EnableRecvTtl ();
Another option here would be to just add all of the above methods to the
existing src/node/udp.h and src/node/tcp.h classes with an extra
Ptr<Socket> argument:
class Udp ...
{
public:
/* Implement the linux IP_PKTINFO socket option. */
virtual void EnablePktInfo (Ptr<Socket> socket) = 0;
virtual void DisablePktInfo (Ptr<Socket> socket) = 0;
virtual bool IsPktInfoEnabled (Ptr<Socket> socket) = 0;
};
The former approach feels slightly more "right".
4) How is ancillary information supported ? It is attached as a tag to
each packet whether it is sent or received.
5) posix sockets are extended with:
int simu_recv (int fd, Ptr<Packet> packet, int flags);
int simu_send (int fd, Ptr<Packet> packet, int flags);
ssize_t simu_write (int fd, Ptr<Packet> packet);
ssize_t simu_read (int fd, Ptr<Packet> packet);
Mathieu
More information about the Ns-developers
mailing list