1 //
2 // Copyright (c) 2010-2023 Antmicro
3 // Copyright (c) 2011-2015 Realtime Embedded
4 //
5 // This file is licensed under the MIT License.
6 // Full license text is available in 'licenses/MIT.txt'.
7 //
8 #if PLATFORM_LINUX
9 using System;
10 using System.Runtime.InteropServices;
11 using System.Text;
12 using Mono.Unix.Native;
13 using Mono.Unix;
14 using Antmicro.Renode.Utilities;
15 using System.Net.NetworkInformation;
16 using Antmicro.Renode.Logging;
17 
18 namespace Antmicro.Renode.TAPHelper
19 {
20     public class TAPTools
21     {
22         private const int O_RDWR                  = 2;
23         private const int IFNAMSIZ                = 0x10;
24         private const int TUNSETIFF               = 1074025674;
25         private const int TUNSETPERSIST           = 0x400454cb;
26         private const UInt16 IFF_TUN              = 0x1;
27         private const UInt16 IFF_TAP_IFF_NO_PI    = 0x0002 | 0x1000;
28         private const int IFR_SIZE                = 80;
29 
30         private const int SIOCSIFFLAGS            = 0x8914;
31         private const int SIOCGIFFLAGS            = 0x8913;
32         private const UInt16 IFF_UP               = 1;
33 
DoesInterfaceExist(string name)34         private static bool DoesInterfaceExist(string name)
35         {
36             var ifaces = NetworkInterface.GetAllNetworkInterfaces();
37             foreach(var iface in ifaces)
38             {
39                 if(iface.Name == name)
40                 {
41                     return true;
42                 }
43             }
44             return false;
45         }
46 
Up_TUNTAP(IntPtr ifrIn)47         private static int Up_TUNTAP(IntPtr ifrIn)
48         {
49             int err = 0;
50 
51             // Bring TAP up - we need to create a regular socket for this
52             int sock = LibCWrapper.Socket(2, 2, 0); //AF_INET, SOCK_DGRAM
53             if(sock == -1)
54             {
55                 Logger.Log(LogLevel.Debug, "Could not create ioctl socket, error {0}", Marshal.GetLastWin32Error());
56                 return sock;
57             }
58 
59             // make copy of ifrIn, so our ioctls don't clobber the donor
60             var ifr = Marshal.AllocHGlobal(IFR_SIZE);
61             LibCWrapper.Strcpy(ifr, ifrIn);
62             try
63             {
64                 if((err = LibCWrapper.Ioctl(sock, SIOCGIFFLAGS, ifr)) < 0)
65                 {
66                     Logger.Log(LogLevel.Debug, "Could not get flags on TUN/TAP interface, error {0}", Marshal.GetLastWin32Error());
67                     return err;
68                 }
69 
70                 var currentFlags = BitConverter.ToUInt16(Marshal.ReadInt16(ifr, IFNAMSIZ).AsRawBytes(), 0);
71 
72                 // Only try this if TAP is down
73                 if((currentFlags & IFF_UP) == 0)
74                 {
75                     currentFlags |= IFF_UP;
76                     Marshal.WriteInt16(ifr, IFNAMSIZ, BitConverter.ToInt16(currentFlags.AsRawBytes(), 0));
77 
78                     if((err = LibCWrapper.Ioctl(sock, SIOCSIFFLAGS, ifr)) < 0)
79                     {
80                         Logger.Log(LogLevel.Debug, "Could not activate TUN/TAP interface, error {0}", Marshal.GetLastWin32Error());
81                         return err;
82                     }
83                 }
84             }
85             finally
86             {
87                 LibCWrapper.Close(sock);
88                 Marshal.FreeHGlobal(ifr);
89             }
90 
91             return err;
92         }
93 
Open_TUNTAP(IntPtr dev, UInt16 flags, bool persistent)94         private static int Open_TUNTAP(IntPtr dev, UInt16 flags, bool persistent)
95         {
96             var fd = LibCWrapper.Open("/dev/net/tun", O_RDWR);
97             if(fd < 0)
98             {
99                 Logger.Log(LogLevel.Debug, "Could not open /dev/net/tun, error: {0}", Marshal.GetLastWin32Error());
100                 return fd;
101             }
102 
103             var ifr = Marshal.AllocHGlobal(IFR_SIZE); // we need 40 bytes, but we allocate a bit more
104             try
105             {
106                 var memory = new byte[IFR_SIZE];
107                 Array.Clear(memory, 0, IFR_SIZE);
108 
109                 var bytes = BitConverter.GetBytes(flags);
110                 Array.Copy(bytes, 0, memory, IFNAMSIZ, 2);
111 
112                 bool exists = false;
113                 if(dev != IntPtr.Zero)
114                 {
115                     string devname = Marshal.PtrToStringAnsi(dev);
116                     exists = DoesInterfaceExist(devname);
117 
118                     var devBytes = Encoding.ASCII.GetBytes(devname);
119                     Array.Copy(devBytes, memory, Math.Min(devBytes.Length, IFNAMSIZ));
120                 }
121 
122                 Marshal.Copy(memory, 0, ifr, IFR_SIZE);
123 
124                 int err = 0;
125                 if((err = LibCWrapper.Ioctl(fd, TUNSETIFF, ifr)) < 0)
126                 {
127                     Logger.Log(LogLevel.Debug, "Could not set TUNSETIFF, error: {0}", Marshal.GetLastWin32Error());
128                     LibCWrapper.Close(fd);
129                     return err;
130                 }
131 
132                 if(persistent)
133                 {
134                     if((err = LibCWrapper.Ioctl(fd, TUNSETPERSIST, 1)) < 0)
135                     {
136                         Logger.Log(LogLevel.Debug, "Could not set TUNSETPERSIST, error: {0}", Marshal.GetLastWin32Error());
137                         LibCWrapper.Close(fd);
138                         return err;
139                     }
140                 }
141 
142                 // If TAP was created by us, we try to bring it up
143                 if(!exists)
144                 {
145                     if((err = Up_TUNTAP(ifr)) < 0)
146                     {
147                         Logger.Log(LogLevel.Debug, "Could not bring device up, do it manually.");
148                         LibCWrapper.Close(fd);
149                         return err;
150                     }
151                 }
152 
153                 LibCWrapper.Strcpy(dev, ifr);
154             }
155             finally
156             {
157                 Marshal.FreeHGlobal(ifr);
158             }
159 
160             return fd;
161         }
162 
OpenTUN(IntPtr dev, bool persistent = false)163         public static int OpenTUN(IntPtr dev, bool persistent = false)
164         {
165             return Open_TUNTAP(dev, IFF_TUN, persistent);
166         }
167 
OpenTAP(IntPtr dev, bool persistent = false)168         public static int OpenTAP(IntPtr dev, bool persistent = false)
169         {
170             return Open_TUNTAP(dev, IFF_TAP_IFF_NO_PI, persistent);
171         }
172     }
173 }
174 #endif
175