0

I'm trying to port some netlink socket code from C to C#. The code is the same as in the link:

First, we port sockaddr_nl structure (c source):

[StructLayout(LayoutKind.Sequential)]
public struct sockaddr_nl
{
    public ushort nl_family;
    public ushort nl_pad;
    public uint nl_pid;
    public uint nl_groups;
}

Then, get a socket fd using

var fd = -1;
fd = UnixFiller.socket(
    (int) UnixAddressFamily.AF_NETLINK,
    (int) UnixSocketType.SOCK_RAW | 0x80000,
    0 /* NETLINK_ROUTE */
);
if(fd < 0)
    FatalError("Failed to create routing socket.");

UnixFiller.socket is just a DLLImport from libc. This call succeeds as expected.

However, the next step would be to cast the expressive sockaddr_nl structure to the family + byte array structure sockaddr consumed by bind:

bind(fd, (struct sockaddr*)&address, sizeof(address));

Where sockaddr is

struct sockaddr {
    sa_family_t sa_family;  /* address family, AF_xxx   */
    char        sa_data[14];    /* 14 bytes of protocol address */
};

Which, as far as I know, would be this in C#:

[StructLayout(LayoutKind.Sequential)]
public struct sockaddr
{
    public ushort sa_familiy;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)] byte[] sa_data;
}

Question

First, how should the bind import look like?

[DllImport("libc", SetLastError = true, EntryPoint = "bind")]
public static extern int bind(int sockfd, ??? addr, int addrlen);

??? = unknown.

Next, how can I port the cast from sockaddr_nl to sockaddr in order to pass the right format to bind?

turbo
  • 1,233
  • 14
  • 36

1 Answers1

0

You can't cast between sockaddr_nl and sockaddr because you have a reference (sa_data) that is going to get in the way. If it was a fixed buffer instead (i.e. raw data directly in the struct), then it would probably be fine, but then you need to use unsafe throughout. However, I'm unclear what that would look like; It looks like you mean that the 14 bytes of sa_data shuld be the contents of sockaddr_nl ? (I only count 12 bytes, note)

To write a struct over a byte[], you can use unsafe code:

static unsafe void Main()
{
    var val = new sockaddr_nl
    {
        nl_family = 1, nl_pad = 2, nl_pid = 3, nl_groups = 4
    };
    byte[] data = new byte[14];
    Console.WriteLine("size of struct: " + sizeof(sockaddr_nl));
    // show value before
    Console.WriteLine(BitConverter.ToString(data));
    // thunk over the data
    fixed (byte* p = data)
    {
        *((sockaddr_nl*)p) = val;
    }
    // show value after
    Console.WriteLine(BitConverter.ToString(data));
}

This has the output:

size of struct: 12
00-00-00-00-00-00-00-00-00-00-00-00-00-00
01-00-02-00-03-00-00-00-04-00-00-00-00-00
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Correct, `sockaddr_nl` is 12 bytes, while `sockaddr` is 16. Both have the initial family value (`sa_family`), which as far as I understand, stays the same when casted. Then the data field array will contain the pid and group field of `sockaddr_nl`. `nl_pad` is just there to pad the struct size. I'm basically just trying to implement this construct: `(struct sockaddr*)&address` – turbo Jun 05 '17 at 14:59