Using SocketCAN in .NET Core

By | February 4, 2021

SocketCAN is a powerful tool for creating CAN Bus oriented applications on Linux. Popular choices for interfacing with SocketCAN are of course C/C++ or Python using the python-can package. As a huge fan of both SocketCAN and C#, I challenged myself to write the current Wikipedia example entirely in C#. In order to achieve this, .NET has Platform Invocation Services (P/Invoke) that allows managed code to call unmanaged code. For example, operating system libraries or 3rd party libraries written in an unmanaged language like C/C++ can be accessed by a managed .NET language such as C# using P/Invoke. This has come up in my professional career of developing vehicle network communication tools where many vehicle communication libraries are typically written in C or C++. P/Invoke can be leveraged in order to bridge the gap between an application written entirely in C# (and optionally XAML) and an unmanaged device API.

Here is the SocketCAN sample code from Wikipedia that I will be porting to C#:

int main(void)
{
	int s;
	int nbytes;
	struct sockaddr_can addr;
	struct can_frame frame;
	struct ifreq ifr;

	const char *ifname = "vcan0";

	if((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) == -1) {
		perror("Error while opening socket");
		return -1;
	}

	strcpy(ifr.ifr_name, ifname);
	ioctl(s, SIOCGIFINDEX, &ifr);
	
	addr.can_family  = AF_CAN;
	addr.can_ifindex = ifr.ifr_ifindex;

	printf("%s at index %d\n", ifname, ifr.ifr_ifindex);

	if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
		perror("Error in socket bind");
		return -2;
	}

	frame.can_id  = 0x123;
	frame.can_dlc = 2;
	frame.data[0] = 0x11;
	frame.data[1] = 0x22;

	nbytes = write(s, &frame, sizeof(struct can_frame));

	printf("Wrote %d bytes\n", nbytes);
	
	return 0;
}

The SocketCAN code above makes use of several constants. There are constants for the Address Family, Protocol Family, the Socket Type, and later an ioctl SIOCGIFINDEX used for looking up an interface index by name. I have defined these constants in a simple static class.

public static class Constants
{
    // Address Family CAN
    public const int AF_CAN = 29; // Per socket.h
    // Protocol Family CAN
    public const int PF_CAN = 29; // Per socket.h
    // Raw Protocol Interface
    public const int SOCK_RAW = 3; // Per socket_type.h
    // Socket Configuration Control: name -> if_index mapping
    public const int SIOCGIFINDEX = 0x8933; // Per ioctls.h
}

SocketCAN also defines several different Protocol Types that can be used within the CAN Protocol Family. The Protocol Types combined with the Protocol Family and Socket Type constants above are what we need to pass into the socket libc function. In this case it makes the most sense to utilize an enum in order to define the Protocol Types in C#.

public enum ProtocolType 
{
    /* RAW sockets */
    CAN_RAW	= 1,
    /* Broadcast Manager */
    CAN_BCM	= 2, 
    /* VW Transport Protocol v1.6 */
    CAN_TP16	= 3,
    /* VW Transport Protocol v2.0 */
    CAN_TP20	= 4, 
    /* Bosch MCNet */
    CAN_MCNET	= 5, 
    /* ISO 15765-2 Transport Protocol */
    CAN_ISOTP	= 6,
    /* SAE J1939 */
    CAN_J1939   = 7,
    CAN_NPROTO	= 8,
}

The ioctl function in libc makes use of an interface request structure called ifreq and is defined in if.h.

struct ifreq
  {
# define IFHWADDRLEN	6
# define IFNAMSIZ	IF_NAMESIZE
    union
      {
	char ifrn_name[IFNAMSIZ];	/* Interface name, e.g. "en0".  */
      } ifr_ifrn;

    union
      {
	struct sockaddr ifru_addr;
	struct sockaddr ifru_dstaddr;
	struct sockaddr ifru_broadaddr;
	struct sockaddr ifru_netmask;
	struct sockaddr ifru_hwaddr;
	short int ifru_flags;
	int ifru_ivalue;
	int ifru_mtu;
	struct ifmap ifru_map;
	char ifru_slave[IFNAMSIZ];	/* Just fits the size */
	char ifru_newname[IFNAMSIZ];
	__caddr_t ifru_data;
      } ifr_ifru;
  };

The members we care about in each union are interface name and index:

# define ifr_name	ifr_ifrn.ifrn_name   /* interface name */
# define ifr_ifindex	ifr_ifru.ifru_ivalue /* interface index */

The C# Ifreq class below represents the above ifreq struct in C. The StructLayout attribute is used to control the memory layout of the unmanaged type. The Sequential option takes the members in the managed type and places them in the same order as they appear in unmanaged memory. Additionally, we specify that the Name string needs to be marshaled as a fixed-length character array of size 16.

// Interface Request Structure used for Socket IOCTLs
[StructLayout(LayoutKind.Sequential)]
public class Ifreq
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=16)]
    public string Name;

    public int IfIndex { get; set; }

    public Ifreq(string name)
    {
        Name = name;
        IfIndex = 0;
    }
}

The bind function in libc is passed a sockaddr_can struct that specifies the address family and index for the CAN interface. There is also the ISO-TP CAN Transmit and Response ID information that we will not make use of in this demo, but it is necessary to be aware of these members for marshalling purposes.

struct sockaddr_can {
        sa_family_t can_family;
        int         can_ifindex;
        union {
                /* transport protocol class address info (e.g. ISOTP) */
                struct { canid_t rx_id, tx_id; } tp;

                /* reserved for future CAN protocols address information */
        } can_addr;
};

Creating the managed class counterpart for this struct is about as easy as it gets as all members of the C struct are integer-based. We specify the Sequential StructLayout to ensure the unmanaged type is properly laid out in memory.

[StructLayout(LayoutKind.Sequential)]
public class SockAddrCan
{
    public ushort CanFamily { get; set; }
    public int CanIfIndex { get; set; }
    public uint RxId { get; set; }
    public uint TxId { get; set; }
}

The last struct we need to model is can_frame which is what we will be sending to the write function of libc.

struct can_frame {
        canid_t can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */
        __u8    can_dlc; /* frame payload length in byte (0 .. 8) */
        __u8    __pad;   /* padding */
        __u8    __res0;  /* reserved / padding */
        __u8    __res1;  /* reserved / padding */
        __u8    data[8] __attribute__((aligned(8)));
};

The can_frame is simple to model as well in C# with the exception that we have to specify that the Data byte array is marshaled as an array of elements of size 8. Again, we use Sequential StructLayout to dictate how the unmanaged type should be laid out in memory by using the order defined in the C# class counterpart.

[StructLayout(LayoutKind.Sequential)]
public class CanFrame
{
    public uint CanId { get; set; }      
    public byte Dlc { get; set; }  
    public byte Pad { get; set; }
    public byte Res0 { get; set; } 
    public byte Res1 { get; set; }
    
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=8)]
    public byte[] Data;

    public CanFrame()
    {
        Data = new byte[8];
    }
}

Lastly, there is the libc function calls socket, ioctl, bind, and write that we need to invoke. To do this we use the DllImport attribute and specify libc as the library we are using. We also set SetLastError to true in order to potentially utilize errno via Marshal.GetLastWin32Error.

[DllImport("libc", EntryPoint="socket", SetLastError=true)]
public static extern IntPtr Socket(int addressFamily, int socketType, ProtocolType protocolType);

[DllImport("libc", EntryPoint="ioctl", SetLastError=true)]
public static extern int Ioctl(IntPtr socketHandle, int request, [In][Out] Ifreq ifreq);

[DllImport("libc", EntryPoint="bind", SetLastError=true)]
public static extern int Bind(IntPtr socketHandle, SockAddrCan addr, int addrSize);

[DllImport("libc", EntryPoint="write", SetLastError=true)]
public static extern int Write(IntPtr socketHandle, CanFrame frame, int frameSize);

Putting this altogether we get the following C# port of the original C code.

static void Main(string[] args)
{
    IntPtr socketHandle = NativeMethods.Socket(Constants.PF_CAN, Constants.SOCK_RAW, ProtocolType.CAN_RAW);
    Console.WriteLine($"Socket Handle: {socketHandle}");

    if (socketHandle != new IntPtr(-1))
    {
        var ifr = new Ifreq("vcan0");
        int ioctlResult = NativeMethods.Ioctl(socketHandle, Constants.SIOCGIFINDEX, ifr);
        Console.WriteLine($"Ioctl Return Code: {ioctlResult}");
        
        if (ioctlResult != -1)
        {
            var addr = new SockAddrCan()
            {
                CanFamily = Constants.AF_CAN,
                CanIfIndex = ifr.IfIndex,
            };

            Console.WriteLine($"{ifr.Name} at index {ifr.IfIndex}");

            int bindResult = NativeMethods.Bind(socketHandle, addr, Marshal.SizeOf(typeof(SockAddrCan)));
            Console.WriteLine($"Bind Return Code: {bindResult}");
            if (bindResult != -1)
            {
                var canFrame = new CanFrame()
                {
                    CanId = 0x123,
                    Dlc = 2,
                };
                canFrame.Data[0] = 0x11;
                canFrame.Data[1] = 0x22;

                int nBytes = NativeMethods.Write(socketHandle, canFrame, Marshal.SizeOf(typeof(CanFrame)));
                Console.WriteLine($"Wrote {nBytes} bytes");
            }
        }
    }
}

Output from this application will look something like this:

Socket Handle: 27
Ioctl Return Code: 0
vcan0 at index 4
Bind Return Code: 0
Wrote 16 bytes

If we listen with candump on vcan0 and then run this program we will see the following:

candump vcan0
vcan0  123   [2]  11 22

Now that we have confirmed that the application outputs the correct CAN message out onto the bus, the porting process is complete. This code is a great start to incorporating SocketCAN into applications written in C# using .NET and what is laid out here can certainly be expanded upon. Thanks as always for reading. The C# source code from this article can be downloaded here.

12 thoughts on “Using SocketCAN in .NET Core

  1. Dmitry Peskin

    Thank you for the article,

    I was able to send test messages, but how I can use this method for listening of the massages from other devices?
    I need to send some message amd wait for the answers.

    Reply
    1. derek Post author

      Hi Dmitry, can-utils provides candump and cansniffer out of the box which are excellent for that purpose. Are you trying to listen to inter-ECU traffic or listen to another Tester Y-cabled into the vehicle DLC?

      Reply
      1. Davide

        Amazing! With your example I’m able to write messages to the network but How to read the message from the C# program?

        Reply
  2. Deepika

    Hi Derek,
    This is exactly which I was looking for. Thanks.

    I want to consume CAN -Utils apis in my ASP.net core web application.
    I have almost no experience with C files.
    To be able to import these dlls, do I need to do anything extra?

    Currently while running this piece of code for .net core 3.1 I get this error:
    System.DllNotFoundException: ‘Unable to load DLL ‘libc’ or one of its dependencies: The specified module could not be found.

    Can you help me out here?

    Thanks
    Deepika

    Reply
  3. Deepika Bohra

    Hi Derek,
    Thank you for this useful post.
    This post make me believe that Can-utils C library for linux can be used for C# .net core application.

    Currently I want to develop a ASP.net core application which can consume all the APIS of CAN-UTILS same as you did for CANSEND.c

    What do you think, to achieve this I need to write all consumer files in C# and do dllimport?
    How do I achieve candump or receiving of message from CAN command on my .Net application?

    Can you help me out here?

    Reply
      1. Jerry Kirk

        Derek,
        Did you compare the cpu load of your socket can library with the CanRaw one in the Iot.DeviceSocketCan library?

        Reply
  4. Jeff Leach

    Hi Derek,

    This looks really interesting. I’m interested in having a cross platform application (windows and Ubuntu) using either CAN Bus or Modbus. I was hoping to use C#. It seams like there is probably a lot out there for modbus and not allot for CAN. This looks really interesting. Would SocketCANSharp work for an application that needed to run in windows and Linux? Or would it only work in Linux? I have never used CAN before so I’m not sure how long it would take me to figure this out. I’m hoping I can take the visual studio published app and run it directly on windows or linux.

    Would SocketCanSharp run this way? Would you recommend another solution?

    Looks really interesting.

    Best Regards,
    Jeff

    Reply
  5. Hasnae

    Hello Mr. Will,
    I am currently doing my Master thesis. I want to develop a HMI based on Raspberry pi and can bus protocol and an inverter. I have added your library SocketCANSharp in my project. I can now read values from the inverter through the CAN BUS and now I want to write over the can bus and I don’t know how can I program this, programming with C#.
    can you please help me
    with kind regards
    Hasnae

    Translated with http://www.DeepL.com/Translator (free version)

    Reply
  6. Hasnae

    Hi Derek
    I can now read values through can bus using your library SocketCANSharp and now want to write values through can bus. do you have any idea how i can do this while i am programming nit c#
    Hasnae

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.