Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programmatically add route

I write a simple utility that adds a route for specific interface. Code is very simple:

using System;
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Text;

class Program
{
    static void Main(string[] args)
    {
        const string firstConnection = "xxxx.yyyyy";
        const string routeAddMask = "route add XX.XX.XX.XX mask 255.255.255.255 XXX.XXX.XXX.XXX METRIC 99 IF {0}";
        StartProcess("rasdial", firstConnection);
        var interfaceId = GetInterfaceId(firstConnection);
        string routeAdd = string.Format(routeAddMask, interfaceId);
        StartProcess("route", routeAdd);
    }

    private static int GetInterfaceId(string name)
    {
        NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces();
        var adapter = Array.Find(adapters, ni => ni.Name == name);
        var props = adapter.GetIPProperties().GetIPv4Properties();
        return props.Index;
    }

    private static void StartProcess(string name, string args)
    {
        var process = new Process
                   {
                       StartInfo = new ProcessStartInfo(name, args)
                                   {
                                       UseShellExecute = false,
                                       RedirectStandardOutput = true,
                                       StandardOutputEncoding = Encoding.ASCII
                                   }
                   };
        process.Start();
        while (!process.HasExited)
        {
            Console.WriteLine(process.StandardOutput.ReadToEnd());
        }
        process.WaitForExit();
        Console.WriteLine("Process {0} finished, exit code = {1}", name, process.ExitCode);
    }
}

rasdial works fine and I get interface number, but when I'm running route I get an error:

Invalid MASK generates an error, that is when (DEST & MASK) != DEST.

I think problem is that process start args are not passed because I get the same error while sending null instead of routeAdd parameter meanwhile this command works fine when i'm prompting it in cmd.

Executable is running as Administrator.

I'm trying to create it with winapi, but it fails too

const string firstConnection = "someconnect";
StartProcess("rasdial", firstConnection);
var interfaceIndex = GetInterfaceIndex(firstConnection); //Everything is fine

var route = new MIB_IPFORWARDROW
            {
                dwForwardDest = BitConverter.ToUInt32(IPAddress.Parse("XX.XX.XX.XX").GetAddressBytes(), 0),
                dwForwardMask = BitConverter.ToUInt32(IPAddress.Parse("255.255.255.255").GetAddressBytes(), 0),
                dwForwardNextHop = BitConverter.ToUInt32(IPAddress.Parse("XXX.XXX.XXX.XXX").GetAddressBytes(), 0),
                dwForwardMetric1 = 99,
                dwForwardIfIndex = interfaceIndex
            };
var ipForwardEntry = RouteInterop.CreateIpForwardEntry(ref route);
Console.WriteLine(ipForwardEntry);

returns ERROR_INVALID_PARAMETER

like image 456
Alex Zhukovskiy Avatar asked Apr 13 '15 08:04

Alex Zhukovskiy


2 Answers

I know that question is not very complex, but I didn't encounter any explanation in the internet, this is why I think this answer would be helpful and this is why I'm writing it instead of deleting my initial question.

You just should specify more parameters to make it work

    var route = new MIB_IPFORWARDROW
                {
                    dwForwardDest = BitConverter.ToUInt32(IPAddress.Parse("XX.XX.XX.XX").GetAddressBytes(), 0),
                    dwForwardMask = BitConverter.ToUInt32(IPAddress.Parse("255.255.255.255").GetAddressBytes(), 0),
                    dwForwardNextHop = BitConverter.ToUInt32(IPAddress.Parse("XXX.XXX.XXX.XXX").GetAddressBytes(), 0),
                    dwForwardMetric1 = 99,
                    dwForwardType =  ForwardType.Indirect,
                    dwForwardProto =  ForwardProtocol.NetMGMT,
                    dwForwardAge = 0,
                    dwForwardIfIndex = interfaceIndex
                };
    var ipForwardEntry = RouteInterop.CreateIpForwardEntry(ref route);
like image 176
Alex Zhukovskiy Avatar answered Oct 11 '22 20:10

Alex Zhukovskiy


Elaborating on Alex Zhukovskiy answer as I've combined his answer with a bunch of MSDN articles. The code below uses the Win32 API's, specifically the "iphlpapi.h" module, the comprehensive documentation can be found in this link on managing routing. The PMIB_IPFORWARDTABLE structure and enums are documented here.

The primary methods for mimicking the "route print", "route add", and "route delete" command are

  • GetIpForwardTable - Gets the network routing table, "route PRINT"
  • CreateIpForwardEntry - Add Route, like "route ADD"
  • DeleteIpFowardEntry - Delete Route, like "route DELETE"
  • SetIpForwardEntry - Modify existing route, like "route CHANGE"

The comprehensive code using C# can be found in this github repository (Ip4RouteTable) - https://github.com/CodeCowboyOrg/Ip4RouteTable

Setting up the Win32 API Calls in C#

internal static class NativeMethods
{
    [DllImport("iphlpapi", CharSet = CharSet.Auto)]
    public extern static int GetIpForwardTable(IntPtr /*PMIB_IPFORWARDTABLE*/ pIpForwardTable, ref int /*PULONG*/ pdwSize, bool bOrder);

    [DllImport("iphlpapi", CharSet = CharSet.Auto)]
    //public extern static int CreateIpForwardEntry(ref /*PMIB_IPFORWARDROW*/ Ip4RouteTable.PMIB_IPFORWARDROW pRoute);  Can do by reference or by Pointer
    public extern static int CreateIpForwardEntry(IntPtr /*PMIB_IPFORWARDROW*/ pRoute);

    [DllImport("iphlpapi", CharSet = CharSet.Auto)]
    public extern static int DeleteIpForwardEntry(IntPtr /*PMIB_IPFORWARDROW*/ pRoute);
}

public class Ip4RouteTable
{
    [ComVisible(false), StructLayout(LayoutKind.Sequential)]
    internal struct IPForwardTable
    {
        public uint Size;

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public PMIB_IPFORWARDROW[] Table;
    };

    [ComVisible(false), StructLayout(LayoutKind.Sequential)]
    internal struct PMIB_IPFORWARDROW
    {
        internal uint /*DWORD*/ dwForwardDest;
        internal uint /*DWORD*/ dwForwardMask;
        internal uint /*DWORD*/ dwForwardPolicy;
        internal uint /*DWORD*/ dwForwardNextHop;
        internal uint /*DWORD*/ dwForwardIfIndex;
        internal uint /*DWORD*/ dwForwardType;
        internal uint /*DWORD*/ dwForwardProto;
        internal uint /*DWORD*/ dwForwardAge;
        internal uint /*DWORD*/ dwForwardNextHopAS;
        internal uint /*DWORD*/ dwForwardMetric1;
        internal uint /*DWORD*/ dwForwardMetric2;
        internal uint /*DWORD*/ dwForwardMetric3;
        internal uint /*DWORD*/ dwForwardMetric4;
        internal uint /*DWORD*/ dwForwardMetric5;
    };

    static IPForwardTable ReadIPForwardTable(IntPtr tablePtr)
    {
        var result = (IPForwardTable)Marshal.PtrToStructure(tablePtr, typeof(IPForwardTable));

        PMIB_IPFORWARDROW[] table = new PMIB_IPFORWARDROW[result.Size];
        IntPtr p = new IntPtr(tablePtr.ToInt64() + Marshal.SizeOf(result.Size));
        for (int i = 0; i < result.Size; ++i)
        {
            table[i] = (PMIB_IPFORWARDROW)Marshal.PtrToStructure(p, typeof(PMIB_IPFORWARDROW));
            p = new IntPtr(p.ToInt64() + Marshal.SizeOf(typeof(PMIB_IPFORWARDROW)));
        }
        result.Table = table;

        return result;
    }

    public static void RoutePrint(bool testing)
    {
        var fwdTable = IntPtr.Zero;
        int size = 0;
        var result = NativeMethods.GetIpForwardTable(fwdTable, ref size, true);
        fwdTable = Marshal.AllocHGlobal(size);

        result = NativeMethods.GetIpForwardTable(fwdTable, ref size, true);

        var forwardTable = ReadIPForwardTable(fwdTable);

        Marshal.FreeHGlobal(fwdTable);

        Console.Write("\tNumber of entries: {0}\n", forwardTable.Size);

        for (int i = 0; i < forwardTable.Table.Length; ++i)
        {
            Console.Write("\n\tRoute[{0}] Dest IP: {1}\n", i, new IPAddress((long)forwardTable.Table[i].dwForwardDest).ToString());
            Console.Write("\tRoute[{0}] Subnet Mask: {1}\n", i, new IPAddress((long)forwardTable.Table[i].dwForwardMask).ToString());
            Console.Write("\tRoute[{0}] Next Hop: {1}\n", i, new IPAddress((long)forwardTable.Table[i].dwForwardNextHop).ToString());
            Console.Write("\tRoute[{0}] If Index: {1}\n", i, forwardTable.Table[i].dwForwardIfIndex);
            Console.Write("\tRoute[{0}] Type: {1}\n", i, forwardTable.Table[i].dwForwardType);
            Console.Write("\tRoute[{0}] Proto: {1}\n", i, forwardTable.Table[i].dwForwardProto);
            Console.Write("\tRoute[{0}] Age: {1}\n", i, forwardTable.Table[i].dwForwardAge);
            Console.Write("\tRoute[{0}] Metric1: {1}\n", i, forwardTable.Table[i].dwForwardMetric1);
        }

    }

    public static void RoutePrint()
    {
        List<Ip4RouteEntry> routeTable = GetRouteTable();
        RoutePrint(routeTable);
    }


    public static void RoutePrint(List<Ip4RouteEntry> routeTable)
    {
        Console.WriteLine("Route Count: {0}", routeTable.Count);
        Console.WriteLine("{0,18} {1,18} {2,18} {3,5} {4,8} ", "DestinationIP", "NetMask", "Gateway", "IF", "Metric");
        foreach (Ip4RouteEntry entry in routeTable)
        {
            Console.WriteLine("{0,18} {1,18} {2,18} {3,5} {4,8} ", entry.DestinationIP, entry.SubnetMask, entry.GatewayIP, entry.InterfaceIndex, entry.Metric);
        }
    }


    public static List<Ip4RouteEntry> GetRouteTable()
    {
        var fwdTable = IntPtr.Zero;
        int size = 0;
        var result = NativeMethods.GetIpForwardTable(fwdTable, ref size, true);
        fwdTable = Marshal.AllocHGlobal(size);

        result = NativeMethods.GetIpForwardTable(fwdTable, ref size, true);

        var forwardTable = ReadIPForwardTable(fwdTable);

        Marshal.FreeHGlobal(fwdTable);


        List<Ip4RouteEntry> routeTable = new List<Ip4RouteEntry>();
        for (int i = 0; i < forwardTable.Table.Length; ++i)
        {
            Ip4RouteEntry entry = new Ip4RouteEntry();
            entry.DestinationIP = new IPAddress((long)forwardTable.Table[i].dwForwardDest);
            entry.SubnetMask = new IPAddress((long)forwardTable.Table[i].dwForwardMask);
            entry.GatewayIP = new IPAddress((long)forwardTable.Table[i].dwForwardNextHop);
            entry.InterfaceIndex = Convert.ToInt32(forwardTable.Table[i].dwForwardIfIndex);
            entry.ForwardType = Convert.ToInt32(forwardTable.Table[i].dwForwardType);
            entry.ForwardProtocol = Convert.ToInt32(forwardTable.Table[i].dwForwardProto);
            entry.ForwardAge = Convert.ToInt32(forwardTable.Table[i].dwForwardAge);
            entry.Metric = Convert.ToInt32(forwardTable.Table[i].dwForwardMetric1);
            routeTable.Add(entry);
        }
        return routeTable;
    }

The C# wrapper functions

 public class Ip4RouteEntry
{
    public IPAddress DestinationIP { get; set; }
    public IPAddress SubnetMask { get; set; }
    public IPAddress GatewayIP { get; set; }
    public int InterfaceIndex { get; set; }
    public int ForwardType { get; set; }
    public int ForwardProtocol { get; set; }
    public int ForwardAge { get; set; }
    public int Metric { get; set; }
}

    public static void RoutePrint()
    {
        List<Ip4RouteEntry> routeTable = GetRouteTable();
        RoutePrint(routeTable);
    }


    public static void RoutePrint(List<Ip4RouteEntry> routeTable)
    {
        Console.WriteLine("Route Count: {0}", routeTable.Count);
        Console.WriteLine("{0,18} {1,18} {2,18} {3,5} {4,8} ", "DestinationIP", "NetMask", "Gateway", "IF", "Metric");
        foreach (Ip4RouteEntry entry in routeTable)
        {
            Console.WriteLine("{0,18} {1,18} {2,18} {3,5} {4,8} ", entry.DestinationIP, entry.SubnetMask, entry.GatewayIP, entry.InterfaceIndex, entry.Metric);
        }
    }


    public static List<Ip4RouteEntry> GetRouteTable()
    {
        var fwdTable = IntPtr.Zero;
        int size = 0;
        var result = NativeMethods.GetIpForwardTable(fwdTable, ref size, true);
        fwdTable = Marshal.AllocHGlobal(size);

        result = NativeMethods.GetIpForwardTable(fwdTable, ref size, true);

        var forwardTable = ReadIPForwardTable(fwdTable);

        Marshal.FreeHGlobal(fwdTable);


        List<Ip4RouteEntry> routeTable = new List<Ip4RouteEntry>();
        for (int i = 0; i < forwardTable.Table.Length; ++i)
        {
            Ip4RouteEntry entry = new Ip4RouteEntry();
            entry.DestinationIP = new IPAddress((long)forwardTable.Table[i].dwForwardDest);
            entry.SubnetMask = new IPAddress((long)forwardTable.Table[i].dwForwardMask);
            entry.GatewayIP = new IPAddress((long)forwardTable.Table[i].dwForwardNextHop);
            entry.InterfaceIndex = Convert.ToInt32(forwardTable.Table[i].dwForwardIfIndex);
            entry.ForwardType = Convert.ToInt32(forwardTable.Table[i].dwForwardType);
            entry.ForwardProtocol = Convert.ToInt32(forwardTable.Table[i].dwForwardProto);
            entry.ForwardAge = Convert.ToInt32(forwardTable.Table[i].dwForwardAge);
            entry.Metric = Convert.ToInt32(forwardTable.Table[i].dwForwardMetric1);
            routeTable.Add(entry);
        }
        return routeTable;
    }


    public static bool RouteExists(string destinationIP)
    {
        List<Ip4RouteEntry> routeTable = Ip4RouteTable.GetRouteTable();
        Ip4RouteEntry routeEntry = routeTable.Find(i => i.DestinationIP.ToString().Equals(destinationIP));
        return (routeEntry != null);
    }


    public static List<Ip4RouteEntry> GetRouteEntry(string destinationIP)
    {
        List<Ip4RouteEntry> routeTable = Ip4RouteTable.GetRouteTable();
        List<Ip4RouteEntry> routeMatches = routeTable.FindAll(i => i.DestinationIP.ToString().Equals(destinationIP));
        return routeMatches;
    }


    public static List<Ip4RouteEntry> GetRouteEntry(string destinationIP, string mask)
    {
        List<Ip4RouteEntry> routeTable = Ip4RouteTable.GetRouteTable();
        List<Ip4RouteEntry> routeMatches = routeTable.FindAll(i => i.DestinationIP.ToString().Equals(destinationIP) && i.SubnetMask.ToString().Equals(mask));
        return routeMatches;
    }


    public static void CreateRoute(Ip4RouteEntry routeEntry)
    {

        var route = new PMIB_IPFORWARDROW
        {
            dwForwardDest = BitConverter.ToUInt32(IPAddress.Parse(routeEntry.DestinationIP.ToString()).GetAddressBytes(), 0),
            dwForwardMask = BitConverter.ToUInt32(IPAddress.Parse(routeEntry.SubnetMask.ToString()).GetAddressBytes(), 0),
            dwForwardNextHop = BitConverter.ToUInt32(IPAddress.Parse(routeEntry.GatewayIP.ToString()).GetAddressBytes(), 0),
            dwForwardMetric1 = 99,
            dwForwardType = Convert.ToUInt32(3), //Default to 3
            dwForwardProto = Convert.ToUInt32(3), //Default to 3
            dwForwardAge = 0,
            dwForwardIfIndex = Convert.ToUInt32(routeEntry.InterfaceIndex)
        };

        IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PMIB_IPFORWARDROW)));
        try
        {
            Marshal.StructureToPtr(route, ptr, false);
            var status = NativeMethods.CreateIpForwardEntry(ptr);
        }
        finally
        {
            Marshal.FreeHGlobal(ptr);
        }

    }

    public static void CreateRoute(string destination, string mask, int interfaceIndex, int metric)
    {
        NetworkAdaptor adaptor = NicInterface.GetNetworkAdaptor(interfaceIndex);
        var route = new PMIB_IPFORWARDROW
        {
            dwForwardDest = BitConverter.ToUInt32(IPAddress.Parse(destination).GetAddressBytes(), 0),
            dwForwardMask = BitConverter.ToUInt32(IPAddress.Parse(mask).GetAddressBytes(), 0),
            dwForwardNextHop = BitConverter.ToUInt32(IPAddress.Parse(adaptor.PrimaryGateway.ToString()).GetAddressBytes(), 0),
            dwForwardMetric1 = Convert.ToUInt32(metric),
            dwForwardType = Convert.ToUInt32(3), //Default to 3
            dwForwardProto = Convert.ToUInt32(3), //Default to 3
            dwForwardAge = 0,
            dwForwardIfIndex = Convert.ToUInt32(interfaceIndex)
        };

        IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PMIB_IPFORWARDROW)));
        try
        {
            Marshal.StructureToPtr(route, ptr, false);
            var status = NativeMethods.CreateIpForwardEntry(ptr);
        }
        finally
        {
            Marshal.FreeHGlobal(ptr);
        }

    }

    public static void DeleteRoute(Ip4RouteEntry routeEntry)
    {

        var route = new PMIB_IPFORWARDROW
        {
            dwForwardDest = BitConverter.ToUInt32(IPAddress.Parse(routeEntry.DestinationIP.ToString()).GetAddressBytes(), 0),
            dwForwardMask = BitConverter.ToUInt32(IPAddress.Parse(routeEntry.SubnetMask.ToString()).GetAddressBytes(), 0),
            dwForwardNextHop = BitConverter.ToUInt32(IPAddress.Parse(routeEntry.GatewayIP.ToString()).GetAddressBytes(), 0),
            dwForwardMetric1 = 99,
            dwForwardType = Convert.ToUInt32(3), //Default to 3
            dwForwardProto = Convert.ToUInt32(3), //Default to 3
            dwForwardAge = 0,
            dwForwardIfIndex = Convert.ToUInt32(routeEntry.InterfaceIndex)
        };

        IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PMIB_IPFORWARDROW)));
        try
        {
            Marshal.StructureToPtr(route, ptr, false);
            var status = NativeMethods.DeleteIpForwardEntry(ptr);
        }
        finally
        {
            Marshal.FreeHGlobal(ptr);
        }

    }

    public static void DeleteRoute(string destinationIP)
    {

        List<Ip4RouteEntry> routeMatches = Ip4RouteTable.GetRouteEntry(destinationIP);
        if (routeMatches == null) return;

        foreach (Ip4RouteEntry routeEntry in routeMatches)
        {
            DeleteRoute(routeEntry);
        }
    }

    public static void DeleteRoute(string destinationIP, string mask)
    {

        List<Ip4RouteEntry> routeMatches = Ip4RouteTable.GetRouteEntry(destinationIP, mask);
        if (routeMatches == null) return;

        foreach (Ip4RouteEntry routeEntry in routeMatches)
        {
            DeleteRoute(routeEntry);
        }
    }


    public static void DeleteRoute(int interfaceIndex)
    {

        var fwdTable = IntPtr.Zero;
        int size = 0;
        var result = NativeMethods.GetIpForwardTable(fwdTable, ref size, true);
        fwdTable = Marshal.AllocHGlobal(size);

        result = NativeMethods.GetIpForwardTable(fwdTable, ref size, true);

        var forwardTable = ReadIPForwardTable(fwdTable);

        Marshal.FreeHGlobal(fwdTable);



        List<PMIB_IPFORWARDROW> filtered = new List<PMIB_IPFORWARDROW>();
        for (int i = 0; i < forwardTable.Table.Length; ++i)
        {
            if (Convert.ToInt32(forwardTable.Table[i].dwForwardIfIndex).Equals(interfaceIndex))
            {
                filtered.Add(forwardTable.Table[i]);
            }
        }

        IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PMIB_IPFORWARDROW)));
        try
        {
            foreach (PMIB_IPFORWARDROW routeEntry in filtered)
            {
                Marshal.StructureToPtr(routeEntry, ptr, false);
                var status = NativeMethods.DeleteIpForwardEntry(ptr);
            }
        }
        finally
        {
            Marshal.FreeHGlobal(ptr);
        }

    }
like image 36
CodeCowboyOrg Avatar answered Oct 11 '22 19:10

CodeCowboyOrg