Socket options?

108 views
Skip to first unread message

Antti Mäkelä

unread,
Oct 20, 2009, 3:33:27 AM10/20/09
to ns-3-users
How do I use the equivalent of setsockopts() in NS-3?

Mostly interested in SO_BINDTODEVICE.so that I could send stuff via
specific interface while ignoring the routing table, but I couldn't
really find *any* socket options either in documentation or just
grepping source code (grep -r BSD_COMPAT src/*). What are available
and how to use them?

Tom Henderson

unread,
Oct 20, 2009, 11:53:13 AM10/20/09
to ns-3-...@googlegroups.com
On Tue, 20 Oct 2009 00:33:27 -0700 (PDT), Antti Mäkelä <zar...@gmail.com>
wrote:

> How do I use the equivalent of setsockopts() in NS-3?

There is no equivalent function. These are intended to be provided either
by attributes or class methods. If you look at tcp-socket.cc you can find
some options such as sndbufsize/rcvbufsize.

When we discussed these a while ago, we agreed that we should typically try
to localize socket options as attributes for the particular type of socket
to which they apply, rather than put them all in class Socket. In the
above case, sndbufsize/rcvbufsize are really TCP-related so they landed in
the TcpSocket class. For SO_BINDTODEVICE, it probably could be supported
by an attribute in socket.cc or an explicit method in class Socket, but
that socket option is not implemented yet.

Tom

>

Antti Mäkelä

unread,
Oct 21, 2009, 6:24:40 AM10/21/09
to ns-3-users
On Oct 20, 6:53 pm, Tom Henderson <t...@tomh.org> wrote:
> When we discussed these a while ago, we agreed that we should typically try
> to localize socket options as attributes for the particular type of socket
> to which they apply, rather than put them all in class Socket.  In the
> above case, sndbufsize/rcvbufsize are really TCP-related so they landed in
> the TcpSocket class.  For SO_BINDTODEVICE, it probably could be supported
> by an attribute in socket.cc or an explicit method in class Socket, but
> that socket option is not implemented yet.

Ok, right now I'm editing routing tables in-situ to reach the same
effect (removing entry, send a packet, re-adding entry). Has anyone
started implementing this feature yet? Might as well try to implement
it myself..
Message has been deleted

Antti Mäkelä

unread,
Oct 27, 2009, 9:36:11 AM10/27/09
to ns-3-users
Ok, now that I have taken a look for doing the
SO_BINDTODEVICE...you
mentioned that appropriate place for this would probably be base
class, but...all the separate socket types (Udp, Raw, Tcp) have their
own SendTo and RecvFrom-functions.

TcpSocket is basically running around it's state machine
(ProcessEvent() function), Udp is obviously much simpler.

Anyway, it seems from what I have deciphered in the source code,
that actual Send-functionality is hidden in XXX-l4-protocol.cc's.
UDP is simpler, TCP has these separate functions for e.g.
sending empty packets. In udp-socket-impl, the socket code itself
actually does some route lookups, udp-socket-impl.cc, line 402
onwards:

route = ipv4->GetRoutingProtocol ()->RouteOutput (p, header,
oif, errno_);
m_udp->Send (p->Copy (), header.GetSource (),
header.GetDestination (),
m_endPoint->GetLocalPort (), port, route);

Same thing *apparently* gets done with TCP, tcp-socket-impl.cc, line
381 onwards.

route = ipv4->GetRoutingProtocol ()->RouteOutput (Ptr<Packet>
(), header, oif, errno_);

So techically, this should work out of the box, actually, just pass
the bound interface as oif. Only problem is that at least
StaticRouting and GlobalRouting's RouteOutput completely ignores the
oif parameter. All they do is parse the header and do a LookupStatic
(destination), ignoring the desired output interface. In the OLSR
there's at least following comment:

// TBD: oif is unused; can be used to restrict the outgoing interface
// of the found route if application bound to a source interface

So, to implement SO_BINDSOCKOPTS, I'd need to either modify the
RouteOutput functions of all the routing protocols to actually care
about the interface if it's present (and considering that indexing
starts at zero, you cannot use zero as "don't care"-value, either...).
Anyway, should be doable but might require some changes to the way
"oif" parameter is thought out (which in turn can mean an API
change).

Also, this only covers the sending of stuff. Anyway, on the receiving
side, a packet ends up at Ipv4-endpoint-demuxer. There is a

Ipv4EndPointDemux::Lookup (Ipv4Address daddr, uint16_t dport,
Ipv4Address saddr, uint16_t sport,
Ptr<Ipv4Interface> incomingInterface)

but the info on incomingInterface is only used for checking if the
packet is broadcast or not. Furthermore, there's logic in the end of
the Lookup that basically says that you don't even need to return the
exact match.

// Here we find the most exact match
if (!retval4.empty()) return retval4;
if (!retval3.empty()) return retval3;
if (!retval2.empty()) return retval2;
return retval1; // might be empty if no matches

If SO_BINDTODEVICE is active, then the demuxer, in my opinion, should
drop stuff that is not wanted coming in from other interfaces.
However, I don't think there's a way to specify that since even
partial matches are accepted! However, if the match is found and
ForwardUp is called, the udp-socket-impl's ForwardUp only receives

UdpSocketImpl::ForwardUp (Ptr<Packet> packet, Ipv4Address ipv4,
uint16_t port)

so the interface is not present here, so you can't do any checking.

So...would this mean that Class endpoint should get two new methods,
that would basically be SetBindToIf and GetBoundIf, and the demuxer
checking routine (Loop starting in ipv4-end-point-demux.cc, line 207)
would check the interface where packet came from against every
endpoint?

As a summary: This may be a socket option, but it sure as heck seems
to require mostly functionality changes in protocol subsystems
instead.

Thoughts? I sort of need this feature but would like some input on
what's the most sensible location for it before opening a bug and
submitting patches..

Tom Henderson

unread,
Oct 27, 2009, 10:11:47 AM10/27/09
to ns-3-...@googlegroups.com

I would probably suggest to overload RouteOutput() to have versions
with/without oif. A question I have is whether we should support a
class that is equivalent to Linux struct flowi (which includes other
items that might constrain the route selection like tos and scope).

So one possibility is, if we introduce some kind of struct/class flowi:

RouteOutput (Ptr<Packet>, header, errno_);
RouteOutput (Ptr<Packet>, header, flowi, errno_);
RouteOutput (Ptr<Packet>, header, oif_, errno_); // calls the "flowi"
version for when oif_ is the only relevant parameter

I am not too concerned about this API change since this is internally
used and presently unimplemented by everyone anyway.

I haven't checked how this is done in Linux/BSD (where access to the
socket data structure is probably available at the demultiplexing point)
but I would recommend that we align the demux accordingly, even if this
is an internal API change.

> As a summary: This may be a socket option, but it sure as heck seems
> to require mostly functionality changes in protocol subsystems
> instead.

Agree.

>
> Thoughts? I sort of need this feature but would like some input on
> what's the most sensible location for it before opening a bug and
> submitting patches..

IMO, we need this feature and should try to add it now.

Tom

Antti Mäkelä

unread,
Oct 28, 2009, 8:18:27 AM10/28/09
to ns-3-users
On Oct 27, 4:11 pm, Tom Henderson <t...@tomh.org> wrote:
> I would probably suggest to overload RouteOutput() to have versions
> with/without oif.  A question I have is whether we should support a
> class that is equivalent to Linux struct flowi (which includes other
> items that might constrain the route selection like tos and scope).
>
> So one possibility is, if we introduce some kind of struct/class flowi:
>
> RouteOutput (Ptr<Packet>, header, errno_);
> RouteOutput (Ptr<Packet>, header, flowi, errno_);
> RouteOutput (Ptr<Packet>, header, oif_, errno_); // calls the "flowi"
> version for when oif_ is the only relevant parameter
>
> I am not too concerned about this API change since this is internally
> used and presently unimplemented by everyone anyway.

Flowi (taken from include/net/flow.h) in Linux sources is basically:

struct flowi {
int oif;
int iif;
__u32 mark;
union {
struct {
__be32 daddr;
__be32 saddr;
__u8 tos;
__u8 scope;
} ip4_u;

struct {
struct in6_addr daddr;
struct in6_addr saddr;
__be32 flowlabel;
} ip6_u;

struct {
__le16 daddr;
__le16 saddr;
__u8 scope;
} dn_u;
} nl_u;

which, if taken directly, might be a bit over the top (also, I can't
find any good documentation on it - is the __u32 mark meant to signify
what's the datatype currently in the nl_u union?).

Anyway, right now oif is an integer, not even Ptr<Ipv4Interface> which
is probably more appropriate. Nevertheless, right now I'm thinking
along the lines of

RouteOutput (Ptr<Packet>, header, oif, errno_, uint32 constraints =
0);

where default would be zero and that would mean that stuff should
happen as before.

I'm using "uint32 constraints" instead of "bool
limit_to_specificed_interface" so that flowi can perhaps be added
later. Now you just need a bunch of macros or some sort of type
definitions, perhaps as bit-fields, say

CONSTRAINT_INTERFACE=0x00000001
CONSTRAINT_TOS=0x00000002
CONSTRAINT_SCOPE=0x00000004

...and so on. Granted, there isn't exactly a socket option that would
make the routing process depend on other aspects of flowi, so I'm not
sure if there is use for going for a more "generic" approach here.

The other alternative I'm thinking might actually be a bit better and
more in sync with the demuxer (see below) would be do the change of
oif from integer to Ptr. So

RouteOutput (Ptr<Packet>, header, Ptr<Ipv4Interface> oif, errno_);

With this, you don't need to include additional "constraint"
parameter. If the oif pointer is zero, there aren't any. If the oif
actually points at something, lookup the route that goes via specified
interface.

> > Also, this only covers the sending of stuff. Anyway, on the receiving
> > side, a packet ends up at Ipv4-endpoint-demuxer. There is a
> I haven't checked how this is done in Linux/BSD (where access to the
> socket data structure is probably available at the demultiplexing point)
> but I would recommend that we align the demux accordingly, even if this
> is an internal API change.

With this in mind, I think I'd do the following changes in ipv4-end-
point.h

Add public member functions:

void SetBoundIf (Ptr<Ipv4Interface>);
Ptr<Ipv4Interface> GetBoundIf ();
bool CompareBoundIf(Ptr<Ipv4Interface>); // Returns true if
m_interface == 0 or matches the parameter

and new private members:

Ptr<Ipv4Interface> m_interface; // Initialized to zero at
constructor

and

change ipv4-end-point-demuxer so that

EndPoints Lookup (Ipv4Address daddr,
uint16_t dport,
Ipv4Address saddr,
uint16_t sport,
Ptr<Ipv4Interface> incomingInterface);

will also check with CompareBoundIf(incomingInterface) if it matches
to the endpoint.

So, I don't think coding this is too hard, however, I'd like to figure
out whether it's better to use uint32 to point to the interface or a
Ptr. I specifically would like the idea of using Names facility to
name node's interfaces (so you could call them e.g. "eth0", or "lan",
"wlan") and then do binding to the name. You can attach such name to
Ptr<Ipv4Interface> object, but you can't do that with a number.

The weird part is that current API doesn't have functions from getting
the Ptr to Ipv4Interface based on index number, or vice versa. You
have stuff like GetNInterfaces(), and
int32_t GetInterfaceForAddress (Ipv4Address addr) const;
int32_t GetInterfaceForPrefix (Ipv4Address addr, Ipv4Mask mask)
const;
int32_t GetInterfaceForDevice (Ptr<const NetDevice> device) const;

but all these just return an integer. I'm a bit baffled why in this
particular case you'd wish to run with integers. There is also no
simple Ptr <Ipv4Interface> GetInterface(int32_t index).

So, full list of changes:

Implement functions to get from ifindex to Ptr<Ipv4Interface> and
vice versa. For ifindex to ptr it's a simple matter of grabbing the
pointer. For vice versa, is there really no simpler path than
{ Ptr<NetDevice> netdev = GetDevice(); return netdev->GetNode()-
>GetObject<Ipv4>()->GetInterfaceForDevice(netdev); } ?

Implement attributes to sockets. For name-based one, there's
MakeStringAccessor. Can I make accessors to objects (in this case,
Ptr<Ipv4Interface>)? Is there some way to make an "union" of attribute
accessors, so that the interface can only be specified once with
whatever method the user chooses, so that you can't e.g. specify a
conflicting name and Ptr at the same time?
Or should this perhaps be done just as member functions and
completely forget about attributes? You already have the Ptr<Socket>
anyway, so just use ->BindToIf() with various formats...Anyway, in the
end this will just pass on the interface pointer to the endpoint class
and store it there.

Change the socket's send functionality so that it grabs the bound if
from Endpoint class and sends it to the routing protocol as a compared
attribute.

Problem with the sockets that I have right now is that, as said, all
the socket types implement their own route lookup calls and their own
sending functions, and lot of the code is replicated across all of
them. However, there's not a "Socket-impl" base class which would have
common stuff to perhaps include this. Then again, right now we have
what, 4 socket types (UDP, TCP, Raw, packet), so maybe it's
acceptable. But could also set up the "socket-impl" which perhaps
should inherit Socket class and be a friend class to all the sub-
types.

Implement the RouteLookup change. If I cook up the above
functionality from going to and from Ptr<Ipv4Interface> and ifindex
integer, I can use the "constraint ="-option and not having to change
stuff on routing protocols that don't necessarily need to be touched
right now (like OLSR).

Implement the Demuxer change. This is reasonably simple - the changes
are shown above.


....time to start coding...
Reply all
Reply to author
Forward
0 new messages