├── .EditorConfig ├── .gitignore ├── CODE_OF_CONDUCT.md ├── LICENSE ├── Magma.sln ├── README.md ├── benchmarks └── Magma.Performance │ ├── Checksum.cs │ ├── Config │ ├── DefaultBenchmarkAttribute.cs │ └── DefaultCoreConfig.cs │ ├── Magma.Performance.csproj │ └── Program.cs ├── data └── rxOutput3.txt ├── docker ├── Dockerfile ├── Ubuntu16.04.txt └── dpdk.txt ├── sample ├── Magma.NetMap.PlaintextApp │ ├── PlaintextApp.csproj │ └── Startup.cs ├── Magma.NetMap.TcpHost │ ├── DuplexPipe.cs │ ├── Magma.NetMap.TcpHost.csproj │ ├── Program.cs │ └── TestConnectionDispatcher.cs └── Magma.WinTun.TcpHost │ ├── Magma.WinTun.TcpHost.csproj │ └── Program.cs ├── samples └── Magma.NetMap.Host │ ├── Magma.NetMap.Host.csproj │ └── Program.cs ├── src ├── Magma.Common │ ├── Checksum.cs │ ├── IPAddress.cs │ └── Magma.Common.csproj ├── Magma.Internet.Icmp │ ├── Code.cs │ ├── ControlMessage.cs │ ├── Header │ │ ├── IcmpV4.cs │ │ └── IcmpV6.cs │ └── Magma.Internet.Icmp.csproj ├── Magma.Internet.Ip │ ├── Header │ │ ├── IPv4.cs │ │ ├── IPv6.cs │ │ └── IPv6Fragment.cs │ ├── Magma.Internet.Ip.csproj │ └── ProtocolNumber.cs ├── Magma.Link │ ├── EtherType.cs │ ├── Header │ │ ├── Arp.cs │ │ └── Ethernet.cs │ ├── MacAddress.cs │ └── Magma.Link.csproj ├── Magma.NetMap │ ├── ExceptionHelper.cs │ ├── Internal │ │ ├── NetMapBufferPool.cs │ │ ├── NetMapHostRxRing.cs │ │ ├── NetMapMemoryWrapper.cs │ │ ├── NetMapOwnedMemory.cs │ │ ├── NetMapPort.cs │ │ ├── NetMapReceiveRing.cs │ │ ├── NetMapRing.cs │ │ └── NetMapTransmitRing.cs │ ├── Interop │ │ ├── Consts.cs │ │ ├── Libc │ │ │ ├── EPoll.cs │ │ │ ├── EventFD.cs │ │ │ ├── FileDescriptors.cs │ │ │ ├── MMap.cs │ │ │ ├── Poll.cs │ │ │ └── TimeEval.cs │ │ ├── Netmap │ │ │ ├── Open.cs │ │ │ └── Rings.cs │ │ ├── RxTxPair.cs │ │ ├── global.cs │ │ └── netmap_if.cs │ ├── Magma.NetMap.csproj │ ├── NetMapTransport.cs │ ├── NetMapTransportFactory.cs │ ├── NetMapTransportOptions.cs │ └── WebHostBuilderNetMapExtensions.cs ├── Magma.Network.Abstractions │ ├── IPacketTransmitter.cs │ ├── Magma.Network.Abstractions.csproj │ └── PacketReceiver.cs ├── Magma.Network │ ├── DefaultPacketReceiver.cs │ ├── Delegates.cs │ └── Magma.Network.csproj ├── Magma.PCap │ ├── FileHeader.cs │ ├── Magma.PCap.csproj │ ├── NetworkKind.cs │ ├── PcapFileWriter.cs │ └── RecordHeader.cs ├── Magma.Transport.Tcp │ ├── Header │ │ ├── Tcp.cs │ │ ├── TcpFlags.cs │ │ ├── TcpHeaderWithOptions.cs │ │ ├── TcpOptionKind.cs │ │ ├── TcpOptionMaxSegmentSize.cs │ │ ├── TcpOptionTimestamp.cs │ │ ├── TcpOptionWindowScale.cs │ │ └── TcpV4PseudoHeader.cs │ ├── Internal │ │ ├── ISocketsTrace.cs │ │ ├── SocketAwaitable.cs │ │ ├── SocketStates.cs │ │ └── SocketsTrace.cs │ ├── Magma.Transport.Tcp.csproj │ ├── Properties │ │ └── SocketsStrings.Designer.cs │ ├── SocketsStrings.resx │ ├── TcpConnection.cs │ ├── TcpConnectionState.cs │ └── TcpTransportReceiver.cs ├── Magma.Transport.Udp │ ├── Header │ │ └── Udp.cs │ └── Magma.Transport.Udp.csproj └── Magma.WinTun │ ├── Internal │ ├── AsyncEventHandle.cs │ ├── WinTunMemoryPool.cs │ ├── WinTunPort.cs │ └── WinTunTransitter.cs │ ├── Interop │ └── WinIO.cs │ ├── Magma.WinTun.csproj │ └── WinTunTransportReceiver.cs ├── test ├── Magma.Common.Facts │ ├── ChecksumFacts.cs │ └── Magma.Common.Facts.csproj ├── Magma.Internet.Ip.Facts │ ├── HexUtils.cs │ ├── IPFacts.cs │ ├── Magma.Internet.Ip.Facts.csproj │ ├── TcpConnectionFacts.cs │ ├── TcpFacts.cs │ └── xunit.runner.json ├── Magma.Link.Facts │ ├── HexUtils.cs │ ├── Magma.Link.Facts.csproj │ ├── ParseEthernetHeader.cs │ └── xunit.runner.json └── Magma.NetMap.Facts │ ├── Magma.NetMap.Facts.csproj │ ├── NetMapInteropFacts.cs │ ├── StructFacts.cs │ └── xunit.runner.json └── version.props /.EditorConfig: -------------------------------------------------------------------------------- 1 | # ASP.NET Core EditorConfig file 2 | 3 | # NOTE: This file focuses on settings Visual Studio 2017 supports natively. For example, VS does not support insert_final_newline. 4 | # We do use it, but it's harder to enforce without a separate VS extension or an editor that supports it. 5 | # See https://docs.microsoft.com/en-us/visualstudio/ide/create-portable-custom-editor-options for more 6 | 7 | # Mark this file as the "root" for everything below this point. This means that editor config files above 8 | # this file will be ignored 9 | root = true 10 | 11 | # Default settings 12 | [*] 13 | indent_style = space 14 | indent_size = 4 15 | charset = utf-8 16 | insert_final_newline = true 17 | 18 | # Unix-only files 19 | [*.sh] 20 | end_of_line = lf 21 | 22 | # 2-space files 23 | [{*.json,*.yml}] 24 | indent_size = 2 25 | 26 | # .NET Code Style Settings 27 | # See https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference 28 | # REVIEW: Should these be errors? warnings? suggestions? 29 | [{*.cs,*.vb}] 30 | dotnet_sort_system_directives_first = true 31 | 32 | # Don't use 'this.'/'Me.' prefix for anything 33 | dotnet_style_qualification_for_field = false:error 34 | dotnet_style_qualification_for_property = false:error 35 | dotnet_style_qualification_for_method = false:error 36 | dotnet_style_qualification_for_event = false:error 37 | 38 | # Use language keywords over framework type names for type references 39 | # i.e. prefer 'string' over 'String' 40 | dotnet_style_predefined_type_for_locals_parameters_members = true:error 41 | dotnet_style_predefined_type_for_member_access = true:error 42 | 43 | # Prefer object/collection initializers 44 | # This is a suggestion because there are cases where this is necessary 45 | dotnet_style_object_initializer = true:suggestion 46 | dotnet_style_collection_initializer = true:suggestion 47 | 48 | # C# 7: Prefer using named tuple names over '.Item1', '.Item2', etc. 49 | dotnet_style_explicit_tuple_names = true:error 50 | 51 | # Prefer using 'foo ?? bar' over 'foo != null ? foo : bar' 52 | dotnet_style_coalesce_expression = true:error 53 | 54 | # Prefer using '?.' over ternary null checking where possible 55 | dotnet_style_null_propagation = true:error 56 | 57 | # Use 'var' in all cases where it can be used 58 | csharp_style_var_for_built_in_types = true:error 59 | csharp_style_var_when_type_is_apparent = true:error 60 | csharp_style_var_elsewhere = true:error 61 | 62 | # C# 7: Prefer using pattern matching over "if(x is T) { var t = (T)x; }" and "var t = x as T; if(t != null) { ... }" 63 | # REVIEW: Since this is a new C# 7 feature that replaces an existing pattern, I'm making it a suggestion 64 | csharp_style_pattern_matching_over_is_with_cast_check = true:warning 65 | csharp_style_pattern_matching_over_as_with_null_check = true:warning 66 | 67 | # C# 7: Prefer using 'out var' where possible 68 | # REVIEW: Since this is a new C# 7 feature that replaces an existing pattern, I'm making it a suggestion 69 | csharp_style_inlined_variable_declaration = true:error 70 | 71 | # C# 7: Use throw expressions when null-checking 72 | # @davidfowl hates them :) 73 | csharp_style_throw_expression = false:error 74 | 75 | # Prefer using "func?.Invoke(args)" over "if(func != null) { func(args); }" 76 | # REVIEW: Maybe an error? 77 | csharp_style_conditional_delegate_call = true:error 78 | 79 | # Newline settings 80 | # Unsure where docs are. Got these from https://github.com/dotnet/roslyn/blob/master/.editorconfig 81 | csharp_new_line_before_open_brace = all 82 | csharp_new_line_before_else = true 83 | csharp_new_line_before_catch = true 84 | csharp_new_line_before_finally = true 85 | csharp_new_line_before_members_in_object_initializers = true 86 | csharp_new_line_before_members_in_anonymous_types = true 87 | 88 | # Prefer expression-bodied methods, constructors, operators, etc. 89 | csharp_style_expression_bodied_methods = true:suggestion 90 | csharp_style_expression_bodied_constructors = true:suggestion 91 | csharp_style_expression_bodied_operators = true:suggestion 92 | csharp_style_expression_bodied_properties = true:suggestion 93 | csharp_style_expression_bodied_indexers = true:suggestion 94 | csharp_style_expression_bodied_accessors = true:suggestion -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | 290 | **/BenchmarkDotNet.Artifacts/* -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at seawardtim@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Tim Seaward 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Magma.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{349471F5-6612-4580-A3F2-9049DCB75114}" 7 | ProjectSection(SolutionItems) = preProject 8 | .EditorConfig = .EditorConfig 9 | version.props = version.props 10 | EndProjectSection 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{34A1DC50-486C-4BB9-9929-1805CA0B0DD0}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B1BA53C8-CCCF-46D5-BA9F-6031811F2E19}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.Link", "src\Magma.Link\Magma.Link.csproj", "{44FBF1F8-FDFB-4EF5-B8F7-81DCAD24B78A}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.Internet.Icmp", "src\Magma.Internet.Icmp\Magma.Internet.Icmp.csproj", "{33BCC702-E02E-4155-906D-F3B376063C17}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.Internet.Ip", "src\Magma.Internet.Ip\Magma.Internet.Ip.csproj", "{972D4793-BCCE-4BFB-BA89-32885E9B2E1F}" 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.Transport.Tcp", "src\Magma.Transport.Tcp\Magma.Transport.Tcp.csproj", "{DBCEC797-0A94-4653-A3B6-22FEE7A6D0CF}" 23 | EndProject 24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.Transport.Udp", "src\Magma.Transport.Udp\Magma.Transport.Udp.csproj", "{4183C47E-2CCB-4C77-9CE0-6D67BB737181}" 25 | EndProject 26 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.NetMap", "src\Magma.NetMap\Magma.NetMap.csproj", "{A913D86A-B8CE-4982-8182-0FBAA1826D68}" 27 | EndProject 28 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.NetMap.Facts", "test\Magma.NetMap.Facts\Magma.NetMap.Facts.csproj", "{8CF7D378-2BDC-4CC0-8659-F43B49A9249C}" 29 | EndProject 30 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{23E375E0-8A4A-4D6A-8C96-9F2046CE9EB0}" 31 | EndProject 32 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.Common", "src\Magma.Common\Magma.Common.csproj", "{A4A05425-7F1E-42CF-AE29-5F6ACBFD8274}" 33 | EndProject 34 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.Network.Abstractions", "src\Magma.Network.Abstractions\Magma.Network.Abstractions.csproj", "{C500EF56-987B-4047-9999-831D0E558231}" 35 | EndProject 36 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.Network", "src\Magma.Network\Magma.Network.csproj", "{5AAC4981-C1AB-471E-A0B3-C0BBFE8CD855}" 37 | EndProject 38 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.Link.Facts", "test\Magma.Link.Facts\Magma.Link.Facts.csproj", "{15262650-814E-4631-A0EE-98D4857BF0B3}" 39 | EndProject 40 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.Common.Facts", "test\Magma.Common.Facts\Magma.Common.Facts.csproj", "{D8E37D7D-E535-4790-B93C-1E60AD624C70}" 41 | EndProject 42 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{0581267E-B53B-4BBE-BB90-80C3CA0C4ACD}" 43 | EndProject 44 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.Performance", "benchmarks\Magma.Performance\Magma.Performance.csproj", "{C2C87D71-4BB3-4DD1-BE4D-35DEBF927290}" 45 | EndProject 46 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.NetMap.TcpHost", "sample\Magma.NetMap.TcpHost\Magma.NetMap.TcpHost.csproj", "{358CD7A8-E78B-4DCD-8640-EA62442755D7}" 47 | EndProject 48 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.Internet.Ip.Facts", "test\Magma.Internet.Ip.Facts\Magma.Internet.Ip.Facts.csproj", "{47D45F49-E8E0-4267-9FDA-8635DA6F5348}" 49 | EndProject 50 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.PCap", "src\Magma.PCap\Magma.PCap.csproj", "{F91B0866-9DF5-488D-AD3E-1E471509C60F}" 51 | EndProject 52 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlaintextApp", "sample\Magma.NetMap.PlaintextApp\PlaintextApp.csproj", "{76D4F5E7-2C04-420D-A43A-B721D2F32904}" 53 | EndProject 54 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Magma.WinTun", "src\Magma.WinTun\Magma.WinTun.csproj", "{503140F9-24F6-41B5-89F2-BB0FB24CCB2E}" 55 | EndProject 56 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Magma.WinTun.TcpHost", "sample\Magma.WinTun.TcpHost\Magma.WinTun.TcpHost.csproj", "{845A5A0B-E59B-46AF-BF95-4FC50D660967}" 57 | EndProject 58 | Global 59 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 60 | Debug|Any CPU = Debug|Any CPU 61 | Release|Any CPU = Release|Any CPU 62 | EndGlobalSection 63 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 64 | {44FBF1F8-FDFB-4EF5-B8F7-81DCAD24B78A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 65 | {44FBF1F8-FDFB-4EF5-B8F7-81DCAD24B78A}.Debug|Any CPU.Build.0 = Debug|Any CPU 66 | {44FBF1F8-FDFB-4EF5-B8F7-81DCAD24B78A}.Release|Any CPU.ActiveCfg = Release|Any CPU 67 | {44FBF1F8-FDFB-4EF5-B8F7-81DCAD24B78A}.Release|Any CPU.Build.0 = Release|Any CPU 68 | {33BCC702-E02E-4155-906D-F3B376063C17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 69 | {33BCC702-E02E-4155-906D-F3B376063C17}.Debug|Any CPU.Build.0 = Debug|Any CPU 70 | {33BCC702-E02E-4155-906D-F3B376063C17}.Release|Any CPU.ActiveCfg = Release|Any CPU 71 | {33BCC702-E02E-4155-906D-F3B376063C17}.Release|Any CPU.Build.0 = Release|Any CPU 72 | {972D4793-BCCE-4BFB-BA89-32885E9B2E1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 73 | {972D4793-BCCE-4BFB-BA89-32885E9B2E1F}.Debug|Any CPU.Build.0 = Debug|Any CPU 74 | {972D4793-BCCE-4BFB-BA89-32885E9B2E1F}.Release|Any CPU.ActiveCfg = Release|Any CPU 75 | {972D4793-BCCE-4BFB-BA89-32885E9B2E1F}.Release|Any CPU.Build.0 = Release|Any CPU 76 | {DBCEC797-0A94-4653-A3B6-22FEE7A6D0CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 77 | {DBCEC797-0A94-4653-A3B6-22FEE7A6D0CF}.Debug|Any CPU.Build.0 = Debug|Any CPU 78 | {DBCEC797-0A94-4653-A3B6-22FEE7A6D0CF}.Release|Any CPU.ActiveCfg = Release|Any CPU 79 | {DBCEC797-0A94-4653-A3B6-22FEE7A6D0CF}.Release|Any CPU.Build.0 = Release|Any CPU 80 | {4183C47E-2CCB-4C77-9CE0-6D67BB737181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 81 | {4183C47E-2CCB-4C77-9CE0-6D67BB737181}.Debug|Any CPU.Build.0 = Debug|Any CPU 82 | {4183C47E-2CCB-4C77-9CE0-6D67BB737181}.Release|Any CPU.ActiveCfg = Release|Any CPU 83 | {4183C47E-2CCB-4C77-9CE0-6D67BB737181}.Release|Any CPU.Build.0 = Release|Any CPU 84 | {A913D86A-B8CE-4982-8182-0FBAA1826D68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 85 | {A913D86A-B8CE-4982-8182-0FBAA1826D68}.Debug|Any CPU.Build.0 = Debug|Any CPU 86 | {A913D86A-B8CE-4982-8182-0FBAA1826D68}.Release|Any CPU.ActiveCfg = Release|Any CPU 87 | {A913D86A-B8CE-4982-8182-0FBAA1826D68}.Release|Any CPU.Build.0 = Release|Any CPU 88 | {8CF7D378-2BDC-4CC0-8659-F43B49A9249C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 89 | {8CF7D378-2BDC-4CC0-8659-F43B49A9249C}.Debug|Any CPU.Build.0 = Debug|Any CPU 90 | {8CF7D378-2BDC-4CC0-8659-F43B49A9249C}.Release|Any CPU.ActiveCfg = Release|Any CPU 91 | {8CF7D378-2BDC-4CC0-8659-F43B49A9249C}.Release|Any CPU.Build.0 = Release|Any CPU 92 | {A4A05425-7F1E-42CF-AE29-5F6ACBFD8274}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 93 | {A4A05425-7F1E-42CF-AE29-5F6ACBFD8274}.Debug|Any CPU.Build.0 = Debug|Any CPU 94 | {A4A05425-7F1E-42CF-AE29-5F6ACBFD8274}.Release|Any CPU.ActiveCfg = Release|Any CPU 95 | {A4A05425-7F1E-42CF-AE29-5F6ACBFD8274}.Release|Any CPU.Build.0 = Release|Any CPU 96 | {C500EF56-987B-4047-9999-831D0E558231}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 97 | {C500EF56-987B-4047-9999-831D0E558231}.Debug|Any CPU.Build.0 = Debug|Any CPU 98 | {C500EF56-987B-4047-9999-831D0E558231}.Release|Any CPU.ActiveCfg = Release|Any CPU 99 | {C500EF56-987B-4047-9999-831D0E558231}.Release|Any CPU.Build.0 = Release|Any CPU 100 | {5AAC4981-C1AB-471E-A0B3-C0BBFE8CD855}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 101 | {5AAC4981-C1AB-471E-A0B3-C0BBFE8CD855}.Debug|Any CPU.Build.0 = Debug|Any CPU 102 | {5AAC4981-C1AB-471E-A0B3-C0BBFE8CD855}.Release|Any CPU.ActiveCfg = Release|Any CPU 103 | {5AAC4981-C1AB-471E-A0B3-C0BBFE8CD855}.Release|Any CPU.Build.0 = Release|Any CPU 104 | {15262650-814E-4631-A0EE-98D4857BF0B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 105 | {15262650-814E-4631-A0EE-98D4857BF0B3}.Debug|Any CPU.Build.0 = Debug|Any CPU 106 | {15262650-814E-4631-A0EE-98D4857BF0B3}.Release|Any CPU.ActiveCfg = Release|Any CPU 107 | {15262650-814E-4631-A0EE-98D4857BF0B3}.Release|Any CPU.Build.0 = Release|Any CPU 108 | {D8E37D7D-E535-4790-B93C-1E60AD624C70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 109 | {D8E37D7D-E535-4790-B93C-1E60AD624C70}.Debug|Any CPU.Build.0 = Debug|Any CPU 110 | {D8E37D7D-E535-4790-B93C-1E60AD624C70}.Release|Any CPU.ActiveCfg = Release|Any CPU 111 | {D8E37D7D-E535-4790-B93C-1E60AD624C70}.Release|Any CPU.Build.0 = Release|Any CPU 112 | {C2C87D71-4BB3-4DD1-BE4D-35DEBF927290}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 113 | {C2C87D71-4BB3-4DD1-BE4D-35DEBF927290}.Debug|Any CPU.Build.0 = Debug|Any CPU 114 | {C2C87D71-4BB3-4DD1-BE4D-35DEBF927290}.Release|Any CPU.ActiveCfg = Release|Any CPU 115 | {C2C87D71-4BB3-4DD1-BE4D-35DEBF927290}.Release|Any CPU.Build.0 = Release|Any CPU 116 | {358CD7A8-E78B-4DCD-8640-EA62442755D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 117 | {358CD7A8-E78B-4DCD-8640-EA62442755D7}.Debug|Any CPU.Build.0 = Debug|Any CPU 118 | {358CD7A8-E78B-4DCD-8640-EA62442755D7}.Release|Any CPU.ActiveCfg = Release|Any CPU 119 | {358CD7A8-E78B-4DCD-8640-EA62442755D7}.Release|Any CPU.Build.0 = Release|Any CPU 120 | {47D45F49-E8E0-4267-9FDA-8635DA6F5348}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 121 | {47D45F49-E8E0-4267-9FDA-8635DA6F5348}.Debug|Any CPU.Build.0 = Debug|Any CPU 122 | {47D45F49-E8E0-4267-9FDA-8635DA6F5348}.Release|Any CPU.ActiveCfg = Release|Any CPU 123 | {47D45F49-E8E0-4267-9FDA-8635DA6F5348}.Release|Any CPU.Build.0 = Release|Any CPU 124 | {F91B0866-9DF5-488D-AD3E-1E471509C60F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 125 | {F91B0866-9DF5-488D-AD3E-1E471509C60F}.Debug|Any CPU.Build.0 = Debug|Any CPU 126 | {F91B0866-9DF5-488D-AD3E-1E471509C60F}.Release|Any CPU.ActiveCfg = Release|Any CPU 127 | {F91B0866-9DF5-488D-AD3E-1E471509C60F}.Release|Any CPU.Build.0 = Release|Any CPU 128 | {76D4F5E7-2C04-420D-A43A-B721D2F32904}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 129 | {76D4F5E7-2C04-420D-A43A-B721D2F32904}.Debug|Any CPU.Build.0 = Debug|Any CPU 130 | {76D4F5E7-2C04-420D-A43A-B721D2F32904}.Release|Any CPU.ActiveCfg = Release|Any CPU 131 | {76D4F5E7-2C04-420D-A43A-B721D2F32904}.Release|Any CPU.Build.0 = Release|Any CPU 132 | {503140F9-24F6-41B5-89F2-BB0FB24CCB2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 133 | {503140F9-24F6-41B5-89F2-BB0FB24CCB2E}.Debug|Any CPU.Build.0 = Debug|Any CPU 134 | {503140F9-24F6-41B5-89F2-BB0FB24CCB2E}.Release|Any CPU.ActiveCfg = Release|Any CPU 135 | {503140F9-24F6-41B5-89F2-BB0FB24CCB2E}.Release|Any CPU.Build.0 = Release|Any CPU 136 | {845A5A0B-E59B-46AF-BF95-4FC50D660967}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 137 | {845A5A0B-E59B-46AF-BF95-4FC50D660967}.Debug|Any CPU.Build.0 = Debug|Any CPU 138 | {845A5A0B-E59B-46AF-BF95-4FC50D660967}.Release|Any CPU.ActiveCfg = Release|Any CPU 139 | {845A5A0B-E59B-46AF-BF95-4FC50D660967}.Release|Any CPU.Build.0 = Release|Any CPU 140 | EndGlobalSection 141 | GlobalSection(SolutionProperties) = preSolution 142 | HideSolutionNode = FALSE 143 | EndGlobalSection 144 | GlobalSection(NestedProjects) = preSolution 145 | {44FBF1F8-FDFB-4EF5-B8F7-81DCAD24B78A} = {34A1DC50-486C-4BB9-9929-1805CA0B0DD0} 146 | {33BCC702-E02E-4155-906D-F3B376063C17} = {34A1DC50-486C-4BB9-9929-1805CA0B0DD0} 147 | {972D4793-BCCE-4BFB-BA89-32885E9B2E1F} = {34A1DC50-486C-4BB9-9929-1805CA0B0DD0} 148 | {DBCEC797-0A94-4653-A3B6-22FEE7A6D0CF} = {34A1DC50-486C-4BB9-9929-1805CA0B0DD0} 149 | {4183C47E-2CCB-4C77-9CE0-6D67BB737181} = {34A1DC50-486C-4BB9-9929-1805CA0B0DD0} 150 | {A913D86A-B8CE-4982-8182-0FBAA1826D68} = {34A1DC50-486C-4BB9-9929-1805CA0B0DD0} 151 | {8CF7D378-2BDC-4CC0-8659-F43B49A9249C} = {B1BA53C8-CCCF-46D5-BA9F-6031811F2E19} 152 | {A4A05425-7F1E-42CF-AE29-5F6ACBFD8274} = {34A1DC50-486C-4BB9-9929-1805CA0B0DD0} 153 | {C500EF56-987B-4047-9999-831D0E558231} = {34A1DC50-486C-4BB9-9929-1805CA0B0DD0} 154 | {5AAC4981-C1AB-471E-A0B3-C0BBFE8CD855} = {34A1DC50-486C-4BB9-9929-1805CA0B0DD0} 155 | {15262650-814E-4631-A0EE-98D4857BF0B3} = {B1BA53C8-CCCF-46D5-BA9F-6031811F2E19} 156 | {D8E37D7D-E535-4790-B93C-1E60AD624C70} = {B1BA53C8-CCCF-46D5-BA9F-6031811F2E19} 157 | {C2C87D71-4BB3-4DD1-BE4D-35DEBF927290} = {0581267E-B53B-4BBE-BB90-80C3CA0C4ACD} 158 | {358CD7A8-E78B-4DCD-8640-EA62442755D7} = {23E375E0-8A4A-4D6A-8C96-9F2046CE9EB0} 159 | {47D45F49-E8E0-4267-9FDA-8635DA6F5348} = {B1BA53C8-CCCF-46D5-BA9F-6031811F2E19} 160 | {F91B0866-9DF5-488D-AD3E-1E471509C60F} = {34A1DC50-486C-4BB9-9929-1805CA0B0DD0} 161 | {76D4F5E7-2C04-420D-A43A-B721D2F32904} = {23E375E0-8A4A-4D6A-8C96-9F2046CE9EB0} 162 | {503140F9-24F6-41B5-89F2-BB0FB24CCB2E} = {34A1DC50-486C-4BB9-9929-1805CA0B0DD0} 163 | {845A5A0B-E59B-46AF-BF95-4FC50D660967} = {23E375E0-8A4A-4D6A-8C96-9F2046CE9EB0} 164 | EndGlobalSection 165 | GlobalSection(ExtensibilityGlobals) = postSolution 166 | SolutionGuid = {99D656D2-FC86-462A-BB4C-610D644ADC62} 167 | EndGlobalSection 168 | EndGlobal 169 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Magma 2 | Under the rocks 3 | 4 | ![](https://aoa.blob.core.windows.net/aspnet/magma.png) 5 | -------------------------------------------------------------------------------- /benchmarks/Magma.Performance/Checksum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using BenchmarkDotNet.Attributes; 4 | using Magma.Network; 5 | 6 | namespace Magma.Performance 7 | { 8 | public class ChecksumBenchmark 9 | { 10 | private byte[] _bytes; 11 | 12 | [Params(16, 64, 256, 1024)] 13 | public int Bytes { get; set; } 14 | 15 | [Benchmark] 16 | public ushort GenerateChecksum() => Checksum.Calculate(ref _bytes[0], _bytes.Length); 17 | 18 | [Benchmark] 19 | public bool ValidateChecksum() => Checksum.IsValid(ref _bytes[0], _bytes.Length); 20 | 21 | [GlobalSetup] 22 | public void GlobalSetup() 23 | { 24 | _bytes = Enumerable.Range(0, Bytes).Select(x => (byte)x).ToArray(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /benchmarks/Magma.Performance/Config/DefaultBenchmarkAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BenchmarkDotNet.Configs; 3 | 4 | namespace Magma.Performance 5 | { 6 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Assembly)] 7 | internal class DefaultBenchmarkAttribute : Attribute, IConfigSource 8 | { 9 | public DefaultBenchmarkAttribute() 10 | { 11 | } 12 | 13 | public IConfig Config => new DefaultCoreConfig(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /benchmarks/Magma.Performance/Config/DefaultCoreConfig.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using BenchmarkDotNet.Columns; 5 | using BenchmarkDotNet.Configs; 6 | using BenchmarkDotNet.Diagnosers; 7 | using BenchmarkDotNet.Engines; 8 | using BenchmarkDotNet.Exporters; 9 | using BenchmarkDotNet.Jobs; 10 | using BenchmarkDotNet.Loggers; 11 | using BenchmarkDotNet.Toolchains.CsProj; 12 | using BenchmarkDotNet.Toolchains.DotNetCli; 13 | using BenchmarkDotNet.Validators; 14 | 15 | namespace Magma.Performance 16 | { 17 | public class DefaultCoreConfig : ManualConfig 18 | { 19 | public DefaultCoreConfig() 20 | { 21 | Add(MarkdownExporter.GitHub); 22 | 23 | Add(MemoryDiagnoser.Default); 24 | Add(StatisticColumn.OperationsPerSecond); 25 | Add(DefaultColumnProviders.Job); 26 | Add(DefaultColumnProviders.Instance); 27 | Add(DefaultColumnProviders.Params); 28 | Add(DefaultColumnProviders.Diagnosers); 29 | 30 | Add(StatisticColumn.Mean); 31 | Add(StatisticColumn.Median); 32 | 33 | Add(StatisticColumn.StdErr); 34 | 35 | Add(BaselineScaledColumn.Scaled); 36 | 37 | Add(ConsoleLogger.Default); 38 | 39 | 40 | Add(JitOptimizationsValidator.FailOnError); 41 | 42 | Add(Job.Core 43 | .With(CsProjCoreToolchain.From(NetCoreAppSettings.NetCoreApp21)) 44 | .With(new GcMode { Server = true }) 45 | .With(RunStrategy.Throughput)); 46 | } 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /benchmarks/Magma.Performance/Magma.Performance.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | 7.3 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /benchmarks/Magma.Performance/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using BenchmarkDotNet.Running; 4 | 5 | namespace Magma.Performance 6 | { 7 | [DefaultBenchmark] 8 | public class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | BenchmarkSwitcher.FromAssembly(typeof(Program).GetTypeInfo().Assembly) 13 | .Run(args, new DefaultCoreConfig()); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:jessie 2 | 3 | RUN apt-get update && apt-get install -y bc git build-essential 4 | RUN apt-get install -y linux-headers-amd64 5 | RUN git clone https://github.com/luigirizzo/netmap.git /usr/src/netmap 6 | RUN cd /usr/src/netmap/LINUX && ./configure --kernel-dir=$(ls -d /usr/src/linux-headers-*-amd64) 7 | RUN cd /usr/src/netmap/LINUX && make 8 | RUN cd /usr/src/netmap/LINUX && make apps 9 | RUN cd /usr/src/netmap/LINUX && make install 10 | 11 | RUN modprobe netmap 12 | 13 | CMD .src/netmap/LINUX/build-apps/pkt-gen/pkt-gen -i eth0 -f tx -l 60 14 | #RUN apt-get -y install kernel-package 15 | #RUN apt-get -y install linux-source 16 | 17 | #RUN git clone https://github.com/luigirizzo/netmap.git 18 | #WORKDIR netmap/LINUX 19 | 20 | #RUN ./configure 21 | #RUN make 22 | #RUN make apps 23 | #RUN make install -------------------------------------------------------------------------------- /docker/Ubuntu16.04.txt: -------------------------------------------------------------------------------- 1 | sudo apt-get update 2 | sudo apt-get install linux-source 3 | git clone https://github.com/luigirizzo/netmap.git 4 | cd netmap 5 | cd LINUX 6 | ./configure 7 | make 8 | sudo make install 9 | sudo modprobe netmap 10 | sudo lsmod | grep net 11 | 12 | # This will disable signing off loading etc to allow it to be all done in userspace 13 | # Without this it won't do passthrough from host correctly 14 | ethtool -K eth0 tx off rx off gso off tso off gro off lro off 15 | 16 | # Test below, will kill the eth0 network 17 | # sudo ./build-apps/pkt-gen/pkt-gen -i eth0 -f tx -l 60 18 | 19 | #To make modprobe perm 20 | sudo echo "netmap" >> /etc/modules 21 | 22 | #to make ethtool perm 23 | sudo echo "ethtool -K eth0 tx off rx off gso off tso off gro off lro off" >> /etc/rc.local 24 | -------------------------------------------------------------------------------- /docker/dpdk.txt: -------------------------------------------------------------------------------- 1 | sudo apt-get update 2 | sudo apt-get install libnuma-dev 3 | sudo apt-get install git build-essential linux-headers-`uname -r` 4 | 5 | git clone http://dpdk.org/git/dpdk 6 | cd dpdk 7 | make config T=x86_64-native-linuxapp-gcc 8 | make 9 | -------------------------------------------------------------------------------- /sample/Magma.NetMap.PlaintextApp/PlaintextApp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | false 6 | true 7 | 8 | 9 | 10 | latest 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /sample/Magma.NetMap.PlaintextApp/Startup.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.IO; 5 | using System.Net; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Microsoft.AspNetCore.Builder; 9 | using Microsoft.AspNetCore.Hosting; 10 | 11 | namespace PlaintextApp 12 | { 13 | public class Startup 14 | { 15 | private static readonly byte[] _helloWorldBytes = Encoding.UTF8.GetBytes("Hello, World!"); 16 | 17 | public void Configure(IApplicationBuilder app) 18 | { 19 | app.Run((httpContext) => 20 | { 21 | var response = httpContext.Response; 22 | response.StatusCode = 200; 23 | response.ContentType = "text/plain"; 24 | 25 | var helloWorld = _helloWorldBytes; 26 | response.ContentLength = helloWorld.Length; 27 | return response.Body.WriteAsync(helloWorld, 0, helloWorld.Length); 28 | }); 29 | } 30 | 31 | public static Task Main(string[] args) 32 | { 33 | var host = new WebHostBuilder() 34 | .UseKestrel(options => 35 | { 36 | options.Listen(IPAddress.Any, 5001); 37 | }) 38 | 39 | .UseNetMap(ops => ops.InterfaceName = "eth0") 40 | 41 | .UseContentRoot(Directory.GetCurrentDirectory()) 42 | .UseStartup() 43 | .Build(); 44 | 45 | return host.RunAsync(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /sample/Magma.NetMap.TcpHost/DuplexPipe.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO.Pipelines; 4 | using System.Text; 5 | 6 | namespace Magma.NetMap.TcpHost 7 | { 8 | internal class DuplexPipe : IDuplexPipe 9 | { 10 | public DuplexPipe(PipeReader reader, PipeWriter writer) 11 | { 12 | Input = reader; 13 | Output = writer; 14 | } 15 | 16 | public PipeReader Input { get; } 17 | 18 | public PipeWriter Output { get; } 19 | 20 | public static DuplexPipePair CreateConnectionPair(PipeOptions inputOptions, PipeOptions outputOptions) 21 | { 22 | var input = new Pipe(inputOptions); 23 | var output = new Pipe(outputOptions); 24 | 25 | var transportToApplication = new DuplexPipe(output.Reader, input.Writer); 26 | var applicationToTransport = new DuplexPipe(input.Reader, output.Writer); 27 | 28 | return new DuplexPipePair(applicationToTransport, transportToApplication); 29 | } 30 | 31 | // This class exists to work around issues with value tuple on .NET Framework 32 | public readonly struct DuplexPipePair 33 | { 34 | public IDuplexPipe Transport { get; } 35 | public IDuplexPipe Application { get; } 36 | 37 | public DuplexPipePair(IDuplexPipe transport, IDuplexPipe application) 38 | { 39 | Transport = transport; 40 | Application = application; 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sample/Magma.NetMap.TcpHost/Magma.NetMap.TcpHost.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | 7 | 8 | 9 | latest 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /sample/Magma.NetMap.TcpHost/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading.Tasks; 4 | 5 | namespace Magma.NetMap.TcpHost 6 | { 7 | class Program 8 | { 9 | static async Task Main(string[] args) 10 | { 11 | var dispatcher = new TestConnectionDispatcher(); 12 | var transport = new NetMapTransport(new IPEndPoint(IPAddress.Any, 80), "eth0", dispatcher); 13 | await transport.BindAsync(); 14 | Console.WriteLine("Hello World!"); 15 | Console.ReadLine(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /sample/Magma.NetMap.TcpHost/TestConnectionDispatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using System.Collections.Generic; 4 | using System.IO.Pipelines; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; 8 | 9 | namespace Magma.NetMap.TcpHost 10 | { 11 | public class TestConnectionDispatcher : IConnectionDispatcher 12 | { 13 | public void OnConnection(TransportConnection connection) 14 | { 15 | // REVIEW: Unfortunately, we still need to use the service context to create the pipes since the settings 16 | // for the scheduler and limits are specified here 17 | var inputOptions = GetInputPipeOptions(connection.MemoryPool, connection.InputWriterScheduler); 18 | var outputOptions = GetOutputPipeOptions(connection.MemoryPool, connection.OutputReaderScheduler); 19 | 20 | var pair = DuplexPipe.CreateConnectionPair(inputOptions, outputOptions); 21 | 22 | // Set the transport and connection id 23 | connection.ConnectionId = Guid.NewGuid().ToString(); 24 | connection.Transport = pair.Transport; 25 | 26 | // This *must* be set before returning from OnConnection 27 | connection.Application = pair.Application; 28 | var ignore = ReadAndRespond(connection); 29 | } 30 | 31 | internal static PipeOptions GetOutputPipeOptions(MemoryPool memoryPool, PipeScheduler readerScheduler) => new PipeOptions 32 | ( 33 | pool: memoryPool, 34 | readerScheduler: readerScheduler, 35 | writerScheduler: PipeScheduler.ThreadPool, 36 | pauseWriterThreshold: (50 * 1024), 37 | resumeWriterThreshold: (50 * 1024), 38 | useSynchronizationContext: false, 39 | minimumSegmentSize: KestrelMemoryPool.MinimumSegmentSize 40 | ); 41 | 42 | // Internal for testing 43 | internal static PipeOptions GetInputPipeOptions(MemoryPool memoryPool, PipeScheduler writerScheduler) => new PipeOptions 44 | ( 45 | pool: memoryPool, 46 | readerScheduler: PipeScheduler.ThreadPool, 47 | writerScheduler: writerScheduler, 48 | pauseWriterThreshold: 0, 49 | resumeWriterThreshold: 0, 50 | useSynchronizationContext: false, 51 | minimumSegmentSize: KestrelMemoryPool.MinimumSegmentSize 52 | ); 53 | 54 | private async Task ReadAndRespond(TransportConnection connection) 55 | { 56 | while (true) 57 | { 58 | var result = await connection.Application.Input.ReadAsync(); 59 | Console.WriteLine("Got actual useful data and it is ---------------------------------"); 60 | Console.WriteLine(Encoding.UTF8.GetString(result.Buffer.First.ToArray())); 61 | connection.Application.Input.AdvanceTo(result.Buffer.End); 62 | 63 | } 64 | //Need to read until we find a End of request and then write back the correct response 65 | } 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /sample/Magma.WinTun.TcpHost/Magma.WinTun.TcpHost.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sample/Magma.WinTun.TcpHost/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using Magma.Transport.Tcp; 4 | using Magma.WinTun.Internal; 5 | 6 | namespace Magma.WinTun.TcpHost 7 | { 8 | class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | var ipEndPoint = new IPEndPoint(IPAddress.Parse("192.168.10.7"), 5001); 13 | var winPort = new WinTunPort>("TunTest", t => new TcpTransportReceiver(ipEndPoint, t, null)); 14 | Console.ReadLine(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /samples/Magma.NetMap.Host/Magma.NetMap.Host.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | true 7 | latest 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /samples/Magma.NetMap.Host/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using System.IO; 4 | using System.Runtime.CompilerServices; 5 | using System.Runtime.InteropServices; 6 | using Magma.Internet.Icmp; 7 | using Magma.Internet.Ip; 8 | using Magma.Network; 9 | using Magma.Network.Abstractions; 10 | using Magma.Network.Header; 11 | 12 | namespace Magma.NetMap.Host 13 | { 14 | class PacketReceiver : IPacketReceiver 15 | { 16 | private int _ringId; 17 | private NetMapTransmitRing _transmitter; 18 | private TextWriter _streamWriter; 19 | 20 | public PacketReceiver(int ringId, NetMapTransmitRing transmitter, bool log, bool loggingToFile) 21 | { 22 | _transmitter = transmitter; 23 | if (log) 24 | { 25 | if (loggingToFile) 26 | { 27 | var filename = Path.Combine(Directory.GetCurrentDirectory(), $"rxOutput{ringId}.txt"); 28 | Console.WriteLine($"Outputing recieved packets to: {filename}"); 29 | _streamWriter = new StreamWriter(filename); 30 | } 31 | else 32 | { 33 | _streamWriter = new StreamWriter(Console.OpenStandardOutput()); 34 | } 35 | } 36 | 37 | _ringId = ringId; 38 | } 39 | 40 | public unsafe T TryConsume(T buffer) where T : IMemoryOwner 41 | { 42 | var input = buffer.Memory.Span; 43 | WriteLine($"---> Received {input.Length} byte packet"); 44 | bool result; 45 | if (Ethernet.TryConsume(input, out var etherIn, out var data)) 46 | { 47 | WriteLine($"{etherIn.ToString()}"); 48 | 49 | if (etherIn.Ethertype == EtherType.IPv4) 50 | { 51 | result = TryConsumeIPv4(in etherIn, data); 52 | } 53 | else 54 | { 55 | WriteLine($"{ etherIn.Ethertype.ToString().PadRight(11)} ---> {BitConverter.ToString(data.ToArray()).MaxLength(60)}"); 56 | // Pass on to host 57 | result = false; 58 | } 59 | } 60 | else 61 | { 62 | WriteLine($"Ether not parsed ---> {BitConverter.ToString(data.ToArray()).MaxLength(60)}"); 63 | // Consume invalid IP packets and don't do further processing 64 | result = true; 65 | } 66 | WriteLine("+--------------------------------------------------------------------------------------+" + Environment.NewLine); 67 | 68 | Flush(); 69 | if (result) 70 | { 71 | buffer.Dispose(); 72 | return default; 73 | } 74 | return buffer; 75 | } 76 | 77 | public unsafe bool TryConsumeIPv4(in Ethernet ethernetFrame, ReadOnlySpan input) 78 | { 79 | if (IPv4.TryConsume(input, out var ipIn, out var data)) 80 | { 81 | WriteLine($"{ipIn.ToString()}"); 82 | 83 | var protocol = ipIn.Protocol; 84 | if (protocol == ProtocolNumber.Tcp) 85 | { 86 | return TryConsumeTcp(in ethernetFrame, in ipIn, data); 87 | } 88 | else if (protocol == ProtocolNumber.Icmp) 89 | { 90 | return TryConsumeIcmp(in ethernetFrame, in ipIn, data); 91 | } 92 | else 93 | { 94 | WriteLine($"Other protocol {protocol.ToString()} ---> {BitConverter.ToString(data.ToArray()).MaxLength(60)}"); 95 | // Pass to host 96 | return false; 97 | } 98 | } 99 | else 100 | { 101 | // Couldn't parse; consume the invalid packet 102 | return true; 103 | } 104 | } 105 | 106 | public unsafe bool TryConsumeTcp(in Ethernet ethernetFrame, in IPv4 ipv4, ReadOnlySpan input) 107 | { 108 | if (Tcp.TryConsume(input, out var tcpIn, out var data)) 109 | { 110 | WriteLine($"{tcpIn.ToString()}"); 111 | WriteLine($"Full packet data {BitConverter.ToString(input.ToArray())}"); 112 | // Pass to host to deal with 113 | return false; 114 | } 115 | else 116 | { 117 | WriteLine($"TCP not parsed ---> {BitConverter.ToString(input.ToArray()).MaxLength(60)}"); 118 | // Couldn't parse; consume the invalid packet 119 | return true; 120 | } 121 | } 122 | 123 | public unsafe bool TryConsumeIcmp(in Ethernet ethernetFrame, in IPv4 ipv4, ReadOnlySpan input) 124 | { 125 | if (!IcmpV4.IsChecksumValid(ref MemoryMarshal.GetReference(input), ipv4.DataLength)) 126 | { 127 | WriteLine($"In Icmp (Checksum Invalid) -> {BitConverter.ToString(input.ToArray())}"); 128 | // Consume packets with invalid checksums; but don't do further processing 129 | return true; 130 | } 131 | 132 | if (IcmpV4.TryConsume(input, out var icmpIn, out var data)) 133 | { 134 | WriteLine($"{icmpIn.ToString()}"); 135 | 136 | if (icmpIn.Code == Code.EchoRequest) 137 | { 138 | if (_transmitter.TryGetNextBuffer(out var txMemory)) 139 | { 140 | var output = txMemory.Span; 141 | 142 | ref byte current = ref MemoryMarshal.GetReference(output); 143 | ref var etherOut = ref Unsafe.As(ref current); 144 | // Swap source & destination 145 | etherOut.Destination = ethernetFrame.Source; 146 | etherOut.Source = ethernetFrame.Destination; 147 | etherOut.Ethertype = ethernetFrame.Ethertype; 148 | 149 | current = ref Unsafe.Add(ref current, Unsafe.SizeOf()); 150 | 151 | ref var ipOuput = ref Unsafe.As(ref current); 152 | ipOuput = ipv4; 153 | // Swap source & destination 154 | ipOuput.SourceAddress = ipv4.DestinationAddress; 155 | ipOuput.DestinationAddress = ipv4.SourceAddress; 156 | // Zero checksum and calcaulate 157 | ipOuput.HeaderChecksum = 0; 158 | ipOuput.HeaderChecksum = Checksum.Calculate(ref current, Unsafe.SizeOf()); 159 | if (!ipOuput.IsChecksumValid()) 160 | { 161 | WriteLine($"Out IP (Checksum Invalid) -> {BitConverter.ToString(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref ipOuput, Unsafe.SizeOf())).ToArray())}"); 162 | } 163 | 164 | current = ref Unsafe.Add(ref current, Unsafe.SizeOf()); 165 | 166 | ref var icmpOutput = ref Unsafe.As(ref current); 167 | icmpOutput = icmpIn; 168 | icmpOutput.Code = Code.EchoReply; 169 | 170 | current = ref Unsafe.Add(ref current, Unsafe.SizeOf()); 171 | // Copy input data to output 172 | data.CopyTo(MemoryMarshal.CreateSpan(ref current, data.Length)); 173 | // Zero checksum and calcaulate 174 | icmpOutput.HeaderChecksum = 0; 175 | icmpOutput.HeaderChecksum = Checksum.Calculate(ref Unsafe.As(ref icmpOutput), ipv4.DataLength); 176 | 177 | if (!IcmpV4.IsChecksumValid(ref Unsafe.As(ref icmpOutput), ipv4.DataLength)) 178 | { 179 | var span = MemoryMarshal.CreateSpan(ref Unsafe.As(ref icmpOutput), ipv4.DataLength); 180 | WriteLine($"{ipv4.DataLength}:{ipOuput.DataLength}:{span.Length}"); 181 | WriteLine($"Out Icmp (Checksum Invalid) -> {BitConverter.ToString(span.ToArray())}"); 182 | } 183 | 184 | var length = Unsafe.ByteOffset(ref Unsafe.As(ref etherOut), ref current).ToInt32() + data.Length; 185 | WriteLine($"<--- Sending {length} byte packet"); 186 | WriteLine(etherOut.ToString()); 187 | WriteLine(ipOuput.ToString()); 188 | WriteLine(icmpOutput.ToString()); 189 | 190 | _transmitter.SendBuffer(txMemory.Slice(0, Unsafe.ByteOffset(ref Unsafe.As(ref etherOut), ref current).ToInt32() + data.Length)); 191 | _transmitter.ForceFlush(); 192 | } 193 | else 194 | { 195 | WriteLine($"TryGetNextBuffer returned false"); 196 | } 197 | 198 | return true; 199 | } 200 | else 201 | { 202 | // Pass other types onto host 203 | return false; 204 | } 205 | } 206 | else 207 | { 208 | WriteLine($"IcmpV4 not parsed ---> {BitConverter.ToString(data.ToArray()).MaxLength(60)}"); 209 | // Consume invalid packets; and don't do further processing 210 | return true; 211 | } 212 | } 213 | 214 | 215 | private void WriteLine(string output) => _streamWriter?.WriteLine(output); 216 | private void Flush() => _streamWriter?.Flush(); 217 | } 218 | 219 | class Program 220 | { 221 | private static int RingId = 0; 222 | 223 | static unsafe void Main(string[] args) 224 | { 225 | var interfaceName = "eth0"; 226 | if (args.Length >= 1) 227 | { 228 | interfaceName = args[0]; 229 | } 230 | 231 | Console.WriteLine($"Ethernet Header length: {Unsafe.SizeOf()}"); 232 | Console.WriteLine($"IP Header length: {Unsafe.SizeOf()}"); 233 | Console.WriteLine($"TCP Header length: {Unsafe.SizeOf()}"); 234 | 235 | var netmap = new NetMapPort(interfaceName, transmitter => new PacketReceiver(RingId++, transmitter, log: true, loggingToFile: true)); 236 | netmap.Open(); 237 | netmap.PrintPortInfo(); 238 | 239 | Console.WriteLine("Started reading"); 240 | while (true) 241 | { 242 | var line = Console.ReadLine(); 243 | if (line == "x") return; 244 | if (!netmap.TransmitRings[0].TryGetNextBuffer(out var buffer)) throw new Exception("Failed to get buffer"); 245 | netmap.TransmitRings[0].SendBuffer(buffer); 246 | Console.WriteLine("Sent buffer!"); 247 | } 248 | } 249 | } 250 | 251 | public static class StringExtensions 252 | { 253 | public static string MaxLength(this string s, int length) 254 | { 255 | if (s.Length <= length) 256 | { 257 | return s; 258 | } 259 | 260 | return s.Substring(0, length - 3) + "..."; 261 | 262 | } 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /src/Magma.Common/Checksum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace Magma.Network 5 | { 6 | public static class Checksum 7 | { 8 | public static bool IsValid(ref byte buffer, int length) 9 | => Calculate(ref buffer, length, 0) == 0 ? true : false; 10 | 11 | 12 | public static ushort Calculate(ref byte buffer, int length) => Calculate(ref buffer, length, 0u); 13 | 14 | public static ulong PartialCalculate(ref byte buffer, int length) => PartialCalculate(ref buffer, length, 0u); 15 | 16 | public static ulong PartialCalculate(ref byte buffer, int length, ulong sum) 17 | { 18 | ref var current = ref buffer; 19 | 20 | while (length >= sizeof(ulong)) 21 | { 22 | length -= sizeof(ulong); 23 | 24 | var ulong0 = Unsafe.As(ref current); 25 | current = ref Unsafe.Add(ref current, sizeof(ulong)); 26 | 27 | // Add with carry 28 | sum += ulong0; 29 | if (sum < ulong0) 30 | { 31 | sum++; 32 | } 33 | } 34 | 35 | if ((length & sizeof(uint)) != 0) 36 | { 37 | var uint0 = Unsafe.As(ref current); 38 | current = ref Unsafe.Add(ref current, sizeof(uint)); 39 | 40 | // Add with carry 41 | sum += uint0; 42 | if (sum < uint0) 43 | { 44 | sum++; 45 | } 46 | } 47 | 48 | if ((length & sizeof(ushort)) != 0) 49 | { 50 | var ushort0 = Unsafe.As(ref current); 51 | current = ref Unsafe.Add(ref current, sizeof(ushort)); 52 | 53 | // Add with carry 54 | sum += ushort0; 55 | if (sum < ushort0) 56 | { 57 | sum++; 58 | } 59 | } 60 | 61 | if ((length & sizeof(byte)) != 0) 62 | { 63 | var byte0 = current; 64 | 65 | // Add with carry 66 | sum += byte0; 67 | if (sum < byte0) 68 | { 69 | sum++; 70 | } 71 | } 72 | return sum; 73 | } 74 | 75 | // Internet Checksum as defined by RFC 791, RFC 793, RFC 1071, RFC 1141, RFC 1624 76 | public static ushort Calculate(ref byte buffer, int length, ulong sum) 77 | { 78 | ref var current = ref buffer; 79 | 80 | while (length >= sizeof(ulong)) 81 | { 82 | length -= sizeof(ulong); 83 | 84 | var ulong0 = Unsafe.As(ref current); 85 | current = ref Unsafe.Add(ref current, sizeof(ulong)); 86 | 87 | // Add with carry 88 | sum += ulong0; 89 | if (sum < ulong0) 90 | { 91 | sum++; 92 | } 93 | } 94 | 95 | if ((length & sizeof(uint)) != 0) 96 | { 97 | var uint0 = Unsafe.As(ref current); 98 | current = ref Unsafe.Add(ref current, sizeof(uint)); 99 | 100 | // Add with carry 101 | sum += uint0; 102 | if (sum < uint0) 103 | { 104 | sum++; 105 | } 106 | } 107 | 108 | if ((length & sizeof(ushort)) != 0) 109 | { 110 | var ushort0 = Unsafe.As(ref current); 111 | current = ref Unsafe.Add(ref current, sizeof(ushort)); 112 | 113 | // Add with carry 114 | sum += ushort0; 115 | if (sum < ushort0) 116 | { 117 | sum++; 118 | } 119 | } 120 | 121 | if ((length & sizeof(byte)) != 0) 122 | { 123 | var byte0 = current; 124 | 125 | // Add with carry 126 | sum += byte0; 127 | if (sum < byte0) 128 | { 129 | sum++; 130 | } 131 | } 132 | 133 | // Fold down to 16 bits 134 | 135 | var uint1 = (uint)(sum >> 32); 136 | var uint2 = (uint)sum; 137 | 138 | // Add with carry 139 | uint1 += uint2; 140 | if (uint1 < uint2) 141 | { 142 | uint1++; 143 | } 144 | 145 | var ushort2 = (ushort)uint1; 146 | var ushort1 = (ushort)(uint1 >> 16); 147 | 148 | // Add with carry 149 | ushort1 = (ushort)(ushort1 + ushort2); 150 | if (ushort1 < ushort2) 151 | { 152 | ushort1++; 153 | } 154 | 155 | // Invert to get ones-complement result 156 | return (ushort)~ushort1; 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/Magma.Common/IPAddress.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Magma.Network 5 | { 6 | public struct IPAddress 7 | { 8 | [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 4)] 9 | public struct V4Address 10 | { 11 | public uint Address; 12 | 13 | public V4Address(byte byte1, byte byte2, byte byte3, byte byte4) => Address = (uint)(byte1 | (byte2 << 8) | (byte3 << 16) | (byte4 << 24)); 14 | 15 | public override string ToString() => $"{Address & 0xff}.{(Address >> 8) & 0xff}.{(Address >> 16) & 0xff}.{(Address >> 24) & 0xff}"; 16 | 17 | public static bool operator ==(V4Address address1, V4Address address2) => address1.Address == address2.Address; 18 | public static bool operator !=(V4Address address1, V4Address address2) => address1.Address != address2.Address; 19 | 20 | public override int GetHashCode() => Address.GetHashCode(); 21 | public override bool Equals(object obj) => obj is V4Address address2 && Address == address2.Address; 22 | } 23 | 24 | [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 16)] 25 | public struct V6Address 26 | { 27 | uint _address0; 28 | uint _address1; 29 | uint _address2; 30 | uint _address3; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Magma.Common/Magma.Common.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | latest 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Magma.Internet.Icmp/Code.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Magma.Internet.Icmp 6 | { 7 | public enum Code : short 8 | { 9 | EchoReply = 0x00 << 8 | ControlMessage.EchoReply, 10 | 11 | DestinationNetworkUnreachable = 0x00 << 8 | ControlMessage.DestinationUnreachable, 12 | DestinationHostUnreachable = 0x01 << 8 | ControlMessage.DestinationUnreachable, 13 | DestinationProtocolUnreachable = 0x02 << 8 | ControlMessage.DestinationUnreachable, 14 | DestinationPortUnreachable = 0x03 << 8 | ControlMessage.DestinationUnreachable, 15 | FragmentationRequired = 0x04 << 8 | ControlMessage.DestinationUnreachable, 16 | SourcRouteFailed = 0x05 << 8 | ControlMessage.DestinationUnreachable, 17 | DestinationNetworkUnknown = 0x06 << 8 | ControlMessage.DestinationUnreachable, 18 | DestinationHostUnknown = 0x07 << 8 | ControlMessage.DestinationUnreachable, 19 | SourceHostIsolated = 0x08 << 8 | ControlMessage.DestinationUnreachable, 20 | NetworkAdministrativelyProhibited = 0x09 << 8 | ControlMessage.DestinationUnreachable, 21 | HostAdministrativelyProhibited = 0x0A << 8 | ControlMessage.DestinationUnreachable, 22 | NetworkUnreachableForToS = 0x0B << 8 | ControlMessage.DestinationUnreachable, 23 | HostUnreachableForToS = 0x0C << 8 | ControlMessage.DestinationUnreachable, 24 | CommunicationAdministrativelyProhibited = 0x0D << 8 | ControlMessage.DestinationUnreachable, 25 | HostPrecedenceViolation = 0x0E << 8 | ControlMessage.DestinationUnreachable, 26 | PrecedenceCutoffInEffect = 0x0F << 8 | ControlMessage.DestinationUnreachable, 27 | 28 | EchoRequest = 0x00 << 8 | ControlMessage.EchoRequest 29 | 30 | // TODO: Other codes 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Magma.Internet.Icmp/ControlMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Magma.Internet.Icmp 6 | { 7 | public enum ControlMessage : byte 8 | { 9 | EchoReply = 0, 10 | Unassigned1 = 1, 11 | Unassigned2 = 2, 12 | DestinationUnreachable = 3, 13 | SourceQuench = 4, 14 | RedirectMessage = 5, 15 | Deprecated6 = 6, 16 | Unassigned7 = 7, 17 | EchoRequest = 8, 18 | RouterAdvertisement = 9, 19 | RouterSolicitation = 10, 20 | TimeExceeded = 11, 21 | ParameterProblem = 12, 22 | Timestamp = 13, 23 | TimestampReply = 14, 24 | InformationRequest = 15, 25 | InformationReply = 16, 26 | AddressMaskRequest = 17, 27 | AddressMaskReply = 18, 28 | 29 | Traceroute = 30 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Magma.Internet.Icmp/Header/IcmpV4.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | using Magma.Internet.Icmp; 5 | 6 | namespace Magma.Network.Header 7 | { 8 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 9 | public struct IcmpV4 10 | { 11 | private short _typeAndCode; 12 | 13 | public ControlMessage Type 14 | { 15 | get => (ControlMessage)(_typeAndCode & 0xFF); 16 | set => _typeAndCode = (short)((byte)value | (_typeAndCode & 0xFF00)); 17 | } 18 | public Code Code 19 | { 20 | get => (Code)_typeAndCode; 21 | set => _typeAndCode = (short)value; 22 | } 23 | 24 | public ushort HeaderChecksum; 25 | public short Identifier; 26 | public short SequenceNumber; 27 | 28 | public static bool TryConsume(ReadOnlySpan input, out IcmpV4 icmp, out ReadOnlySpan data) 29 | { 30 | if (input.Length >= Unsafe.SizeOf()) 31 | { 32 | icmp = Unsafe.As(ref MemoryMarshal.GetReference(input)); 33 | // CRC check 34 | data = input.Slice(Unsafe.SizeOf()); 35 | return true; 36 | } 37 | 38 | data = default; 39 | icmp = default; 40 | return false; 41 | } 42 | 43 | public unsafe override string ToString() 44 | { 45 | return "+- Icmp Datagram ----------------------------------------------------------------------+" + Environment.NewLine + 46 | $"| {Type.ToString()} - {Code} | Id: {System.Net.IPAddress.NetworkToHostOrder(Identifier)} | Seq: {System.Net.IPAddress.NetworkToHostOrder(SequenceNumber)} ".PadRight(87) 47 | + "|"; 48 | } 49 | 50 | public static bool IsChecksumValid(ref byte IcmpStart, int length) => Checksum.IsValid(ref IcmpStart, length); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Magma.Internet.Icmp/Header/IcmpV6.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Magma.Network.Header 6 | { 7 | public struct IcmpV6 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Magma.Internet.Icmp/Magma.Internet.Icmp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | latest 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Magma.Internet.Ip/Header/IPv4.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Magma.Internet.Ip; 6 | 7 | using static Magma.Network.IPAddress; 8 | 9 | namespace Magma.Network.Header 10 | { 11 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 12 | public struct IPv4 13 | { 14 | // No IPv4 Options data in this struct 15 | private byte _versionAndHeaderLength; 16 | private byte _dscpAndEcn; 17 | private ushort _totalLength; 18 | private ushort _identification; 19 | private ushort _flagsAndFragmentOffset; 20 | private byte _ttl; 21 | private ProtocolNumber _protocol; 22 | private ushort _checksum; 23 | private V4Address _sourceIPAdress; 24 | private V4Address _destinationIPAdress; 25 | 26 | /// 27 | /// The first header field in an IP packet is the four-bit version field. For IPv4, this is always equal to 4. 28 | /// 29 | public byte Version 30 | { 31 | get => (byte)(_versionAndHeaderLength >> 4); 32 | set => _versionAndHeaderLength = (byte)(InternetHeaderLength | (value << 4)); 33 | } 34 | 35 | /// 36 | /// The Internet Header Length (IHL) field has 4 bits, which is the number of 32-bit words. 37 | /// 38 | /// 39 | /// Since an IPv4 header may contain a variable number of options, this field specifies the size of the header (this also coincides with the offset to the data). 40 | /// The minimum value for this field is 5, which indicates a length of 5 × 32 bits = 160 bits = 20 bytes. 41 | /// As a 4-bit field, the maximum value is 15 words (15 × 32 bits, or 480 bits = 60 bytes). 42 | /// 43 | public byte InternetHeaderLength 44 | { 45 | get => (byte)(_versionAndHeaderLength & 0b0000_1111); 46 | set 47 | { 48 | TotalLength = (ushort)(value + DataLength); 49 | _versionAndHeaderLength = (byte)((value) | (Version << 4)); 50 | } 51 | } 52 | 53 | /// 54 | /// This field is defined by RFC 2474 (updated by RFC 3168 and RFC 3260) for Differentiated services (DiffServ). 55 | /// New technologies are emerging that require real-time data streaming and therefore make use of the DSCP field. 56 | /// An example is Voice over IP (VoIP), which is used for interactive data voice exchange. 57 | /// 58 | public byte DifferentiatedServicesCodePoint => (byte)(_dscpAndEcn & 0b1111_1100); 59 | 60 | /// 61 | /// This field is defined in RFC 3168 and allows end-to-end notification of network congestion without dropping packets. 62 | /// ECN is an optional feature that is only used when both endpoints support it and are willing to use it. 63 | /// It is only effective when supported by the underlying network. 64 | /// 65 | public byte ExplicitCongestionNotification => (byte)(_dscpAndEcn >> 6); 66 | 67 | public byte DscpAndEcn { get => _dscpAndEcn; set => _dscpAndEcn = value; } 68 | 69 | /// 70 | /// This 16-bit field defines the entire packet size in bytes, including header and data. 71 | /// The minimum size is 20 bytes (header without data) and the maximum is 65,535 bytes. 72 | /// All hosts are required to be able to reassemble datagrams of size up to 576 bytes, but most modern hosts handle much larger packets. 73 | /// 74 | /// 75 | /// Sometimes links impose further restrictions on the packet size, in which case datagrams must be fragmented. 76 | /// Fragmentation in IPv4 is handled in either the host or in routers. 77 | /// 78 | public ushort TotalLength 79 | { 80 | get => (ushort)System.Net.IPAddress.NetworkToHostOrder((short)_totalLength); 81 | set => _totalLength = (ushort)System.Net.IPAddress.HostToNetworkOrder((short)value); 82 | } 83 | 84 | public ushort HeaderLength { get => (ushort)(InternetHeaderLength * 4); set => InternetHeaderLength = (byte)(value / 4); } 85 | public ushort DataLength 86 | { 87 | get => (ushort)(TotalLength - HeaderLength); 88 | set => TotalLength = (ushort)(value + HeaderLength); 89 | } 90 | 91 | /// 92 | /// This field is an identification field and is primarily used for uniquely identifying the group of fragments of a single IP datagram. 93 | /// 94 | /// 95 | /// Some experimental work has suggested using the ID field for other purposes, 96 | /// such as for adding packet-tracing information to help trace datagrams with spoofed source addresses, 97 | /// but RFC 6864 now prohibits any such use. 98 | /// 99 | public ushort Identification { get => _identification; set => _identification = value; } 100 | 101 | /// 102 | /// A three-bit field follows and is used to control or identify fragments. They are (in order, from most significant to least significant): 103 | /// bit 0: Reserved; must be zero. 104 | /// bit 1: Don't Fragment (DF) 105 | /// bit 2: More Fragments(MF) 106 | /// 107 | /// 108 | /// If the DF flag is set, and fragmentation is required to route the packet, then the packet is dropped. 109 | /// This can be used when sending packets to a host that does not have sufficient resources to handle fragmentation. 110 | /// It can also be used for Path MTU Discovery, either automatically by the host IP software, 111 | /// or manually using diagnostic tools such as ping or traceroute. 112 | /// For unfragmented packets, the MF flag is cleared. For fragmented packets, all fragments except the last have the MF flag set. 113 | /// The last fragment has a non-zero Fragment Offset field, differentiating it from an unfragmented packet. 114 | /// 115 | public byte Flags => (byte)(_flagsAndFragmentOffset >> 13); 116 | 117 | public bool DontFragment 118 | { 119 | get => (_flagsAndFragmentOffset & 0b_0100_0000) > 0; 120 | set 121 | { 122 | if (value) 123 | { 124 | _flagsAndFragmentOffset |= 0b_0100_0000; 125 | } 126 | else 127 | { 128 | _flagsAndFragmentOffset &= 0b_1011_1111; 129 | } 130 | } 131 | } 132 | 133 | /// 134 | /// The fragment offset field is measured in units of eight-byte blocks. 135 | /// 136 | /// 137 | /// It is 13 bits long and specifies the offset of a particular fragment relative to the beginning of the original unfragmented IP datagram. 138 | /// The first fragment has an offset of zero. 139 | /// This allows a maximum offset of (213 – 1) × 8 = 65,528 bytes, 140 | /// which would exceed the maximum IP packet length of 65,535 bytes with the header length included (65,528 + 20 = 65,548 bytes). 141 | /// 142 | public ushort FragmentOffset => (ushort)(_flagsAndFragmentOffset & 0b_0001_1111_1111_1111); 143 | 144 | public ushort FlagsAndFragmentOffset { get => _flagsAndFragmentOffset; set => _flagsAndFragmentOffset = value; } 145 | 146 | /// 147 | /// An eight-bit time to live field helps prevent datagrams from persisting n an internet. 148 | /// This field limits a datagram's lifetime and is a hop count - 149 | /// when the datagram arrives at a router, the router decrements the TTL field by one. 150 | /// 151 | public byte TimeToLive { get => _ttl; set => _ttl = value; } 152 | 153 | /// 154 | /// This field defines the protocol used in the data portion of the IP datagram. 155 | /// 156 | /// 157 | /// The Internet Assigned Numbers Authority maintains a list of IP protocol numbers which was originally defined in RFC 790 158 | /// 159 | public ProtocolNumber Protocol { get => _protocol; set => _protocol = value; } 160 | 161 | /// 162 | /// The 16-bit checksum field is used for error-checking of the header. 163 | /// When a packet arrives at a router, the router calculates the checksum of the header and compares it to the checksum field. 164 | /// If the values do not match, the router discards the packet. 165 | /// 166 | /// 167 | /// When a packet arrives at a router, the router decreases the TTL field. Consequently, the router must calculate a new checksum. 168 | /// 169 | public ushort HeaderChecksum 170 | { 171 | get => _checksum; 172 | set => _checksum = value; 173 | } 174 | 175 | /// 176 | /// This field is the IPv4 address of the sender of the packet. 177 | /// Note that this address may be changed in transit by a network address translation device. 178 | /// 179 | public V4Address SourceAddress 180 | { 181 | get => _sourceIPAdress; 182 | set => _sourceIPAdress = value; 183 | } 184 | 185 | /// 186 | /// This field is the IPv4 address of the receiver of the packet. 187 | /// As with the source address, this may be changed in transit by a network address translation device. 188 | /// 189 | public V4Address DestinationAddress 190 | { 191 | get => _destinationIPAdress; 192 | set => _destinationIPAdress = value; 193 | } 194 | 195 | public static bool TryConsume(ReadOnlySpan input, out IPv4 ip, out ReadOnlySpan data, bool doChecksum = true) 196 | { 197 | if (input.Length >= Unsafe.SizeOf()) 198 | { 199 | ip = Unsafe.As(ref MemoryMarshal.GetReference(input)); 200 | var totalSize = ip.TotalLength; 201 | var headerSize = ip.HeaderLength; 202 | if (ip.Version == 4 && (uint)totalSize >= (uint)headerSize && (uint)totalSize == (uint)input.Length && (doChecksum || ip.IsChecksumValid())) 203 | { 204 | data = input.Slice(headerSize); 205 | return true; 206 | } 207 | } 208 | 209 | ip = default; 210 | data = default; 211 | return false; 212 | } 213 | 214 | public static void InitHeader(ref IPv4 header, V4Address source, V4Address destination, ushort dataSize, ProtocolNumber protocol, ushort identification) 215 | { 216 | header.Version = 4; 217 | header.HeaderLength = 20; 218 | header.DscpAndEcn = 0; 219 | header.DestinationAddress = destination; 220 | header.SourceAddress = source; 221 | header.Protocol = protocol; 222 | header.TimeToLive = 45; 223 | header.FlagsAndFragmentOffset = 0; 224 | header.DontFragment = true; 225 | header.DataLength = dataSize; 226 | header.HeaderChecksum = 0; 227 | header.Identification = identification; 228 | header.HeaderChecksum = Checksum.Calculate(ref Unsafe.As(ref header), Unsafe.SizeOf()); 229 | } 230 | 231 | public unsafe override string ToString() 232 | { 233 | return "+- IPv4 Datagram ----------------------------------------------------------------------+" + Environment.NewLine + 234 | $"| {Protocol.ToString().PadRight(11)} | {SourceAddress.ToString()} -> {DestinationAddress.ToString()} | Length: {TotalLength}, H: {HeaderLength}, D: {DataLength}".PadRight(86) + 235 | (IsChecksumValid() ? " " : "X") 236 | + "|"; 237 | } 238 | 239 | public bool IsChecksumValid() => Checksum.IsValid(ref Unsafe.As(ref this), Unsafe.SizeOf()); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/Magma.Internet.Ip/Header/IPv6.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Magma.Network.Header 6 | { 7 | public struct IPv6 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Magma.Internet.Ip/Header/IPv6Fragment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Magma.Network.Header 6 | { 7 | public struct IPv6Fragment 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Magma.Internet.Ip/Magma.Internet.Ip.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | latest 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Magma.Internet.Ip/ProtocolNumber.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Magma.Internet.Ip 6 | { 7 | public enum ProtocolNumber : byte 8 | { 9 | Icmp = 0x01, 10 | Tcp = 0x06, 11 | Udp = 0x11, 12 | 13 | IPv6Encap = 0x29, 14 | IPv6Route = 0x2B, 15 | IPv6Frag = 0x2C, 16 | IPv6Icmp = 0x3A, 17 | IPv6NoNxt = 0x3B, 18 | IPv6Opts = 0x3C 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Magma.Link/EtherType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Magma.Network 6 | { 7 | public enum EtherType : ushort 8 | { 9 | Arp = 0x0608, 10 | IPv4 = 0x0008, 11 | IPv6 = 0xdd86, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Magma.Link/Header/Arp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Magma.Network.Header 6 | { 7 | public struct Arp 8 | { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Magma.Link/Header/Ethernet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | using Magma.Link; 5 | 6 | namespace Magma.Network.Header 7 | { 8 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 9 | public struct Ethernet 10 | { 11 | public MacAddress Destination; 12 | public MacAddress Source; 13 | public EtherType Ethertype; 14 | 15 | public static bool TryConsume(ReadOnlySpan input, out Ethernet ethernetFrame, out ReadOnlySpan data) 16 | { 17 | if (input.Length >= Unsafe.SizeOf()) 18 | { 19 | ethernetFrame = Unsafe.As(ref MemoryMarshal.GetReference(input)); 20 | data = input.Slice(Unsafe.SizeOf(), input.Length - (Unsafe.SizeOf())); 21 | return true; 22 | } 23 | 24 | ethernetFrame = default; 25 | data = default; 26 | return false; 27 | } 28 | 29 | public override string ToString() 30 | { 31 | return "+- Ethernet Frame ---------------------------------------------------------------------+" + Environment.NewLine + 32 | $"| {Ethertype.ToString().PadRight(11)} | {Source.ToString()} -> {Destination.ToString()}".PadRight(87) + "|"; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Magma.Link/MacAddress.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace Magma.Link 7 | { 8 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 9 | public struct MacAddress 10 | { 11 | public static readonly MacAddress Broadcast = new MacAddress() { AddressPart1 = 0xFFFF, AddressPart2 = 0xFFFF, AddressPart3 = 0xFFFF }; 12 | 13 | public ushort AddressPart1; 14 | public ushort AddressPart2; 15 | public ushort AddressPart3; 16 | 17 | public override string ToString() 18 | { 19 | return $"{(AddressPart1 & 0xFF):X2}:{(AddressPart1 >> 8):X2}:{(AddressPart2 & 0xFF):X2}:{(AddressPart2 >> 8):X2}:{(AddressPart3 & 0xFF):X2}:{(AddressPart3 >> 8):X2}"; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Magma.Link/Magma.Link.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | latest 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Magma.NetMap/ExceptionHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Magma.NetMap 6 | { 7 | internal static class ExceptionHelper 8 | { 9 | public static void Throw() where T : Exception, new() => throw new T(); 10 | public static void ThrowInvalidOperation(string message) => throw new InvalidOperationException(message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Internal/NetMapBufferPool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Magma.NetMap.Internal 6 | { 7 | internal class NetMapBufferPool 8 | { 9 | private readonly NetMapOwnedMemory[] _memoryPool; 10 | 11 | public NetMapBufferPool(ushort bufferLength, IntPtr bufferStart, uint numberOfBuffers) 12 | { 13 | _memoryPool = new NetMapOwnedMemory[numberOfBuffers]; 14 | for(var i = 0; i < numberOfBuffers;i++) 15 | { 16 | _memoryPool[i] = new NetMapOwnedMemory(IntPtr.Add(bufferStart, i * bufferLength), bufferLength, (uint)i); 17 | } 18 | } 19 | 20 | public NetMapOwnedMemory GetBuffer(uint bufferIndex) => _memoryPool[bufferIndex]; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Internal/NetMapHostRxRing.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading; 7 | using Magma.NetMap.Interop; 8 | using Magma.Network.Header; 9 | using static Magma.NetMap.Interop.Libc; 10 | 11 | namespace Magma.NetMap.Internal 12 | { 13 | internal sealed class NetMapHostRxRing:NetMapRing 14 | { 15 | private readonly Thread _worker; 16 | private readonly NetMapTransmitRing _transmitRing; 17 | 18 | internal unsafe NetMapHostRxRing(RxTxPair rxTxPair, byte* memoryRegion, long rxQueueOffset, NetMapTransmitRing transmitRing) 19 | : base(rxTxPair, memoryRegion, rxQueueOffset) 20 | { 21 | _transmitRing = transmitRing; 22 | _worker = new Thread(new ThreadStart(ThreadLoop)); 23 | } 24 | 25 | public override void Start() => _worker.Start(); 26 | 27 | internal override void Return(int buffer_index) => throw new NotImplementedException(); 28 | 29 | private void ThreadLoop() 30 | { 31 | ref var ring = ref RingInfo; 32 | while (true) 33 | { 34 | while (!IsRingEmpty()) 35 | { 36 | //Console.WriteLine("Received data on host ring"); 37 | var i = ring.Cursor; 38 | ring.Cursor = RingNext(i); 39 | if(_transmitRing.TryGetNextBuffer(out var copyBuffer)) 40 | { 41 | ref var slot = ref GetSlot(i); 42 | var bufferSource = GetBuffer(slot.buf_idx); 43 | bufferSource.CopyTo(copyBuffer.Span); 44 | _transmitRing.SendBuffer(copyBuffer.Slice(0, slot.len)); 45 | } 46 | ring.Head = RingNext(ring.Head); 47 | } 48 | _rxTxPair.WaitForWork(); 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Internal/NetMapMemoryWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | 4 | namespace Magma.NetMap.Internal 5 | { 6 | internal struct NetMapMemoryWrapper : IMemoryOwner 7 | { 8 | private NetMapOwnedMemory _ownedMemory; 9 | 10 | public NetMapMemoryWrapper(NetMapOwnedMemory ownedMemory) => _ownedMemory = ownedMemory; 11 | 12 | public Memory Memory => _ownedMemory.Memory; 13 | 14 | public void Dispose() => _ownedMemory.Return(); 15 | 16 | public bool IsEmpty => _ownedMemory == null; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Internal/NetMapOwnedMemory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | 4 | namespace Magma.NetMap.Internal 5 | { 6 | internal class NetMapOwnedMemory : MemoryManager 7 | { 8 | private IntPtr _pointer; 9 | private ushort _length; 10 | 11 | public NetMapOwnedMemory(IntPtr pointer, ushort length, uint index) 12 | { 13 | _pointer = pointer; 14 | _length = length; 15 | BufferIndex = index; 16 | } 17 | 18 | internal void Return() => RingId.Return((int)BufferIndex); 19 | 20 | internal uint BufferIndex { get; } 21 | internal NetMapRing RingId { get; set; } 22 | 23 | public override Memory Memory => CreateMemory(_length); 24 | 25 | internal ushort Length { set => _length = value; } 26 | 27 | public unsafe override Span GetSpan() => new Span(_pointer.ToPointer(), _length); 28 | 29 | public unsafe override MemoryHandle Pin(int elementIndex = 0) => new MemoryHandle(IntPtr.Add(_pointer, elementIndex).ToPointer()); 30 | 31 | public override void Unpin() { } 32 | 33 | protected override void Dispose(bool disposing) { } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Internal/NetMapPort.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.CompilerServices; 5 | using System.Text; 6 | using Magma.NetMap.Interop; 7 | using Magma.Network.Abstractions; 8 | using static Magma.NetMap.Interop.Libc; 9 | using static Magma.NetMap.Interop.Netmap; 10 | 11 | namespace Magma.NetMap.Internal 12 | { 13 | internal class NetMapPort : IDisposable 14 | where TPacketReceiver : IPacketReceiver 15 | { 16 | private NetMapReceiveRing[] _receiveRings; 17 | private NetMapTransmitRing[] _transmitRings; 18 | private List _allRings = new List(); 19 | private RxTxPair[] _rxTxPairs; 20 | private NetMapHostRxRing _hostRxRing; 21 | private NetMapTransmitRing _hostTxRing; 22 | private readonly string _interfaceName; 23 | private FileDescriptor _fileDescriptor; 24 | private NetMapRequest _request; 25 | private IntPtr _mappedRegion; 26 | private NetMapInterface _netmapInterface; 27 | private readonly Func _createReceiver; 28 | 29 | public NetMapPort(string interfaceName, Func createReceiver) 30 | { 31 | _interfaceName = interfaceName; 32 | _createReceiver = createReceiver; 33 | } 34 | 35 | public NetMapTransmitRing[] TransmitRings => _transmitRings; 36 | 37 | private IntPtr NetMapInterfaceAddress => IntPtr.Add(_mappedRegion, (int)_request.nr_offset); 38 | 39 | public unsafe void Open() 40 | { 41 | _fileDescriptor = OpenNetMap(_interfaceName, 0, NetMapRequestFlags.NR_REG_NIC_SW, out _request); 42 | 43 | MapMemory(); 44 | SetupRings(); 45 | 46 | var maxBufferId = _allRings.Select(r => r.GetMaxBufferId()).Max(); 47 | var buffersStart = _allRings[0].BufferStart; 48 | var pool = new NetMapBufferPool((ushort)_allRings[0].BufferSize, buffersStart, maxBufferId + 1); 49 | foreach (var ring in _allRings) 50 | { 51 | ring.BufferPool = pool; 52 | ring.Start(); 53 | } 54 | } 55 | 56 | private unsafe void SetupRings() 57 | { 58 | var txOffsets = new long[_netmapInterface.NumberOfTXRings]; 59 | var rxOffsets = new long[_netmapInterface.NumberOfRXRings]; 60 | _rxTxPairs = new RxTxPair[txOffsets.Length + 1]; 61 | var span = new Span(IntPtr.Add(NetMapInterfaceAddress, Unsafe.SizeOf()).ToPointer(), _netmapInterface.NumberOfRXRings + _netmapInterface.NumberOfTXRings + 2); 62 | for (var i = 0; i < txOffsets.Length; i++) 63 | { 64 | txOffsets[i] = span[0]; 65 | span = span.Slice(1); 66 | _rxTxPairs[i] = new RxTxPair(_interfaceName, i, false); 67 | } 68 | _rxTxPairs[txOffsets.Length] = new RxTxPair(_interfaceName, txOffsets.Length, true); 69 | 70 | var txHost = span[0]; 71 | span = span.Slice(1); 72 | 73 | for (var i = 0; i < rxOffsets.Length; i++) 74 | { 75 | rxOffsets[i] = span[0]; 76 | span = span.Slice(1); 77 | } 78 | var rxHost = span[0]; 79 | 80 | _hostTxRing = new NetMapTransmitRing(_rxTxPairs[_rxTxPairs.Length - 1], (byte*)_mappedRegion.ToPointer(), txHost); 81 | _allRings.Add(_hostTxRing); 82 | _transmitRings = new NetMapTransmitRing[txOffsets.Length]; 83 | _receiveRings = new NetMapReceiveRing[rxOffsets.Length]; 84 | for (var i = 0; i < txOffsets.Length; i++) 85 | { 86 | _transmitRings[i] = new NetMapTransmitRing(_rxTxPairs[i], (byte*)_mappedRegion.ToPointer(), txOffsets[i]); 87 | _allRings.Add(_transmitRings[i]); 88 | _receiveRings[i] = new NetMapReceiveRing(_rxTxPairs[i], (byte*)_mappedRegion.ToPointer(), rxOffsets[i], _createReceiver(_transmitRings[i]), _hostTxRing); 89 | _allRings.Add(_receiveRings[i]); 90 | } 91 | _hostRxRing = new NetMapHostRxRing(_rxTxPairs[_rxTxPairs.Length - 1], (byte*)_mappedRegion.ToPointer(), rxHost, _transmitRings[0]); 92 | _allRings.Add(_hostRxRing); 93 | } 94 | 95 | private unsafe void MapMemory() 96 | { 97 | var mapResult = MMap(IntPtr.Zero, _request.nr_memsize, MemoryMappedProtections.PROT_READ | MemoryMappedProtections.PROT_WRITE, MemoryMappedFlags.MAP_SHARED, _fileDescriptor, offset: 0); 98 | 99 | if ((long)mapResult < 0) throw new InvalidOperationException("Failed to map the memory"); 100 | 101 | Console.WriteLine("Mapped the memory region correctly"); 102 | _mappedRegion = mapResult; 103 | _netmapInterface = Unsafe.Read(NetMapInterfaceAddress.ToPointer()); 104 | } 105 | 106 | public void PrintPortInfo() 107 | { 108 | Console.WriteLine($"memsize = {_request.nr_memsize}"); 109 | Console.WriteLine($"tx rings = {_request.nr_tx_rings}"); 110 | Console.WriteLine($"rx rings = {_request.nr_rx_rings}"); 111 | Console.WriteLine($"tx slots = {_request.nr_tx_slots}"); 112 | Console.WriteLine($"rx slots = {_request.nr_rx_slots}"); 113 | Console.WriteLine($"Offset to IF Header {_request.nr_offset}"); 114 | Console.WriteLine($"Interface Nic RX Queues {_netmapInterface.NumberOfRXRings}"); 115 | Console.WriteLine($"Interface Nic TX Queues {_netmapInterface.NumberOfTXRings}"); 116 | Console.WriteLine($"Interface Start of extra buffers {_netmapInterface.ni_bufs_head}"); 117 | } 118 | 119 | protected void Dispose(bool isDisposing) 120 | { 121 | foreach(var pair in _rxTxPairs) 122 | { 123 | pair.Dispose(); 124 | } 125 | if (_mappedRegion != IntPtr.Zero) 126 | { 127 | MUnmap(_mappedRegion, _request.nr_memsize); 128 | _mappedRegion = IntPtr.Zero; 129 | } 130 | if(_fileDescriptor.IsValid) 131 | { 132 | Close(_fileDescriptor); 133 | _fileDescriptor = new FileDescriptor(); 134 | } 135 | } 136 | 137 | public void Dispose() => Dispose(true); 138 | 139 | ~NetMapPort() => Dispose(false); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Internal/NetMapReceiveRing.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Magma.NetMap.Interop; 4 | using Magma.Network.Abstractions; 5 | 6 | namespace Magma.NetMap.Internal 7 | { 8 | internal sealed class NetMapReceiveRing : NetMapRing 9 | where TPacketReceiver : IPacketReceiver 10 | { 11 | private readonly Thread _worker; 12 | private TPacketReceiver _receiver; 13 | private NetMapTransmitRing _hostTxRing; 14 | private readonly object _lock = new object(); 15 | 16 | internal unsafe NetMapReceiveRing(RxTxPair rxTxPair, byte* memoryRegion, long queueOffset, TPacketReceiver receiver, NetMapTransmitRing hostTxRing) 17 | : base(rxTxPair, memoryRegion, queueOffset) 18 | { 19 | _hostTxRing = hostTxRing; 20 | _receiver = receiver; 21 | _worker = new Thread(new ThreadStart(ThreadLoop)); 22 | } 23 | 24 | public override void Start() => _worker.Start(); 25 | 26 | private void ThreadLoop() 27 | { 28 | ref var ring = ref RingInfo; 29 | while (true) 30 | { 31 | while (!IsRingEmpty()) 32 | { 33 | 34 | var i = ring.Cursor; 35 | ref var slot = ref GetSlot(i); 36 | var buffer = _bufferPool.GetBuffer(slot.buf_idx); 37 | buffer.RingId = this; 38 | buffer.Length = slot.len; 39 | ring.Cursor = RingNext(i); 40 | if (!_receiver.TryConsume(new NetMapMemoryWrapper(buffer)).IsEmpty) 41 | { 42 | if(_hostTxRing.TryGetNextBuffer(out var copyMemory)) 43 | { 44 | buffer.Memory.CopyTo(copyMemory); 45 | _hostTxRing.SendBuffer(copyMemory.Slice(0, slot.len)); 46 | } 47 | MoveHeadForward(slot.buf_idx); 48 | } 49 | } 50 | _receiver.FlushPendingAcks(); 51 | //Add a little spin to check 52 | Thread.SpinWait(100); 53 | if (!IsRingEmpty()) continue; 54 | 55 | _rxTxPair.WaitForWork(); 56 | } 57 | } 58 | 59 | private void MoveHeadForward(uint bufferIndex) 60 | { 61 | lock(_lock) 62 | { 63 | ref var ring = ref RingInfo; 64 | ref var slot = ref GetSlot(ring.Head); 65 | if(slot.buf_idx != bufferIndex) 66 | { 67 | slot.buf_idx = bufferIndex; 68 | slot.flags |= Netmap.NetmapSlotFlags.NS_BUF_CHANGED; 69 | } 70 | ring.Head = RingNext(ring.Head); 71 | } 72 | } 73 | 74 | private void PrintSlotInfo(int index) 75 | { 76 | var slot = GetSlot(index); 77 | Console.WriteLine($"Slot {index} bufferIndex {slot.buf_idx} flags {slot.flags} length {slot.len} pointer {slot.ptr}"); 78 | } 79 | 80 | internal override void Return(int buffer_index) => MoveHeadForward((uint)buffer_index); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Internal/NetMapRing.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Threading; 4 | using Magma.NetMap.Interop; 5 | using static Magma.NetMap.Interop.Netmap; 6 | 7 | namespace Magma.NetMap.Internal 8 | { 9 | internal unsafe abstract class NetMapRing 10 | { 11 | protected readonly byte* _memoryRegion; 12 | protected readonly long _queueOffset; 13 | protected readonly int _bufferSize; 14 | 15 | protected readonly int _numberOfSlots; 16 | protected readonly int _ringId; 17 | private readonly NetmapSlot* _rxRing; 18 | protected readonly byte* _bufferStart; 19 | internal RxTxPair _rxTxPair; 20 | internal NetMapBufferPool _bufferPool; 21 | 22 | protected NetMapRing(RxTxPair rxTxPair, byte* memoryRegion, long queueOffset) 23 | { 24 | _rxTxPair = rxTxPair; 25 | _queueOffset = queueOffset; 26 | _memoryRegion = memoryRegion; 27 | var ringInfo = RingInfo; 28 | _bufferSize = (int)ringInfo.BufferSize; 29 | _numberOfSlots = (int)ringInfo.NumberOfSlotsPerRing; 30 | _bufferStart = _memoryRegion + _queueOffset + ringInfo.BuffersOffset; 31 | _ringId = ringInfo.RingId; 32 | 33 | _rxRing = (NetmapSlot*)((long)(_memoryRegion + queueOffset + Unsafe.SizeOf() + 127 + 128) & (~0xFF)); 34 | } 35 | 36 | internal NetMapBufferPool BufferPool { set => _bufferPool = value; } 37 | internal unsafe ref NetmapRing RingInfo => ref Unsafe.AsRef((_memoryRegion + _queueOffset)); 38 | 39 | internal Span GetBuffer(uint bufferIndex) => GetBuffer(bufferIndex, (ushort)_bufferSize); 40 | internal ref NetmapSlot GetSlot(int index) => ref _rxRing[index]; 41 | internal unsafe IntPtr BufferStart => (IntPtr)_bufferStart; 42 | 43 | internal int BufferSize => _bufferSize; 44 | 45 | internal Span GetBuffer(uint bufferIndex, ushort size) 46 | { 47 | var ptr = _bufferStart + (bufferIndex * _bufferSize); 48 | return new Span(ptr, size); 49 | } 50 | 51 | internal int RingNext(int i) => (i + 1 == _numberOfSlots) ? 0 : i + 1; 52 | internal bool IsRingEmpty() 53 | { 54 | ref var ring = ref RingInfo; 55 | return (Volatile.Read(ref ring.Cursor) == Volatile.Read(ref ring.Tail)); 56 | } 57 | 58 | internal int GetCursor() 59 | { 60 | ref var ring = ref RingInfo; 61 | while (true) 62 | { 63 | var cursor = ring.Cursor; 64 | if (RingSpace(cursor) > 0) 65 | { 66 | var newIndex = RingNext(cursor); 67 | if (Interlocked.CompareExchange(ref ring.Cursor, newIndex, cursor) == cursor) 68 | { 69 | return cursor; 70 | } 71 | } 72 | else 73 | { 74 | //No space so we will spin or backpressure 75 | return -1; 76 | } 77 | } 78 | } 79 | 80 | public int RingSpace(int cursor) 81 | { 82 | ref var ring = ref RingInfo; 83 | var ret = ring.Tail - cursor; 84 | if (ret < 0) 85 | ret += _numberOfSlots; 86 | return ret; 87 | } 88 | 89 | internal uint GetMaxBufferId() 90 | { 91 | var max = 0u; 92 | for (var i = 0; i < _numberOfSlots; i++) 93 | { 94 | max = Math.Max(max, _rxRing[i].buf_idx); 95 | } 96 | return max; 97 | } 98 | 99 | internal abstract void Return(int buffer_index); 100 | public abstract void Start(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Internal/NetMapTransmitRing.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Magma.NetMap.Interop; 7 | using Magma.Network.Abstractions; 8 | using static Magma.NetMap.Interop.Netmap; 9 | 10 | namespace Magma.NetMap.Internal 11 | { 12 | internal sealed class NetMapTransmitRing : NetMapRing, IPacketTransmitter 13 | { 14 | private const int SPINCOUNT = 100; 15 | private const int MAXLOOPTRY = 2; 16 | private ManualResetEventSlim _sendEvent = new ManualResetEventSlim(true); 17 | private Queue<(NetMapOwnedMemory ownedMemory, ushort length)> _sendQueue = new Queue<(NetMapOwnedMemory ownedMemory, ushort length)>(1024); 18 | private SpinLock _lock = new SpinLock(enableThreadOwnerTracking: false); 19 | private Thread _flushThread; 20 | 21 | internal unsafe NetMapTransmitRing(RxTxPair rxTxPair, byte* memoryRegion, long queueOffset) 22 | : base(rxTxPair, memoryRegion, queueOffset) => _flushThread = new Thread(FlushLoop); 23 | 24 | public override void Start() => _flushThread.Start(); 25 | 26 | private void FlushLoop() 27 | { 28 | ref var ring = ref RingInfo; 29 | while (true) 30 | { 31 | var dataWritten = false; 32 | lock (_sendQueue) 33 | { 34 | while (_sendQueue.TryDequeue(out var queuedItem)) 35 | { 36 | var newHead = RingNext(ring.Head); 37 | ref var slot = ref GetSlot(ring.Head); 38 | if (slot.buf_idx != queuedItem.ownedMemory.BufferIndex) 39 | { 40 | slot.buf_idx = queuedItem.ownedMemory.BufferIndex; 41 | slot.flags |= NetmapSlotFlags.NS_BUF_CHANGED; 42 | } 43 | slot.len = queuedItem.length; 44 | ring.Head = newHead; 45 | dataWritten = true; 46 | } 47 | } 48 | if(dataWritten) _rxTxPair.ForceFlush(); 49 | if (_sendQueue.Count > 0) continue; 50 | _sendEvent.Wait(); 51 | _sendEvent.Reset(); 52 | 53 | } 54 | } 55 | 56 | public bool TryGetNextBuffer(out Memory buffer) 57 | { 58 | ref var ring = ref RingInfo; 59 | for (var loop = 0; loop < MAXLOOPTRY; loop++) 60 | { 61 | var slotIndex = GetCursor(); 62 | if (slotIndex == -1) 63 | { 64 | Thread.SpinWait(100); 65 | continue; 66 | } 67 | var slot = GetSlot(slotIndex); 68 | var manager = _bufferPool.GetBuffer(slot.buf_idx); 69 | manager.RingId = this; 70 | manager.Length = (ushort)_bufferSize; 71 | buffer = manager.Memory; 72 | return true; 73 | } 74 | buffer = default; 75 | return false; 76 | } 77 | 78 | public Task SendBuffer(ReadOnlyMemory buffer) 79 | { 80 | if (!MemoryMarshal.TryGetMemoryManager(buffer, out NetMapOwnedMemory manager, out var start, out var length)) 81 | { 82 | ExceptionHelper.ThrowInvalidOperation("Invalid buffer used for sendbuffer"); 83 | } 84 | if (start != 0) ExceptionHelper.ThrowInvalidOperation("Invalid start for buffer"); 85 | if (manager.RingId != this) ExceptionHelper.ThrowInvalidOperation($"Invalid ring id, expected {_ringId} actual {manager.RingId}"); 86 | lock (_sendQueue) 87 | { 88 | _sendQueue.Enqueue((manager, (ushort)length)); 89 | _sendEvent.Set(); 90 | } 91 | return Task.CompletedTask; 92 | } 93 | 94 | internal override void Return(int buffer_index) => throw new NotImplementedException(); 95 | 96 | public uint RandomSequenceNumber() => (uint)(new Random().Next()); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Interop/Consts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Magma.NetMap.Interop 6 | { 7 | internal static class Consts 8 | { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Interop/Libc/EPoll.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace Magma.NetMap.Interop 7 | { 8 | internal static partial class Libc 9 | { 10 | [DllImport("libc", EntryPoint = "epoll_wait")] 11 | internal extern static int EPollWait(EPollHandle __epfd, ref EPollEvent __events, int __maxevents, int __timeout); 12 | 13 | [DllImport("libc", EntryPoint = "epoll_ctl")] 14 | internal extern static int EPollControl(EPollHandle __epfd, EPollCommand __op, FileDescriptor __fd, ref EPollEvent __event); 15 | 16 | [DllImport("libc", EntryPoint = "epoll_create1")] 17 | internal extern static EPollHandle EPollCreate(int __flags); 18 | 19 | internal struct EPollHandle 20 | { 21 | public int Pointer; 22 | } 23 | 24 | [StructLayout(LayoutKind.Sequential)] 25 | internal struct EPollData 26 | { 27 | public IntPtr ptr; 28 | public FileDescriptor FileDescriptor; 29 | public uint u32; 30 | public ulong u64; 31 | } 32 | 33 | [StructLayout(LayoutKind.Sequential)] 34 | internal struct EPollEvent 35 | { 36 | public EPollEvents events; /* Epoll events */ 37 | public EPollData data; /* User data variable */ 38 | } 39 | 40 | internal enum EPollCommand : uint 41 | { 42 | EPOLL_CTL_ADD = 1, 43 | EPOLL_CTL_DEL = 2, 44 | EPOLL_CTL_MOD = 3, 45 | } 46 | 47 | [Flags] 48 | internal enum EPollEvents : uint 49 | { 50 | EPOLLIN = 0x001, 51 | EPOLLPRI = 0x002, 52 | EPOLLOUT = 0x004, 53 | EPOLLRDNORM = 0x040, 54 | EPOLLRDBAND = 0x080, 55 | EPOLLWRNORM = 0x100, 56 | EPOLLWRBAND = 0x200, 57 | EPOLLMSG = 0x400, 58 | EPOLLERR = 0x008, 59 | EPOLLHUP = 0x010, 60 | EPOLLRDHUP = 0x2000, 61 | EPOLLEXCLUSIVE = 1u << 28, 62 | EPOLLWAKEUP = 1u << 29, 63 | EPOLLONESHOT = 1u << 30, 64 | EPOLLET = 1u << 31 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Interop/Libc/EventFD.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace Magma.NetMap.Interop 7 | { 8 | internal static partial class Libc 9 | { 10 | [DllImport("libc", EntryPoint = "eventfd_read")] 11 | internal static extern int EventFDRead(FileDescriptor descriptor, ref ulong value); 12 | 13 | [DllImport("libc", EntryPoint = "eventfd_write")] 14 | internal static extern int EventFDWrite(FileDescriptor descriptor, ulong value); 15 | 16 | [DllImport("libc", EntryPoint = "eventfd")] 17 | internal static extern FileDescriptor CreateEventFD(int count, EventFDFlags flags); 18 | 19 | [Flags] 20 | internal enum EventFDFlags : int 21 | { 22 | EFD_SEMAPHORE = 0x00000001, 23 | EFD_CLOEXEC = 0x02000000, 24 | EFD_NONBLOCK = 0x00004000 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Interop/Libc/FileDescriptors.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | using static Magma.NetMap.Interop.Netmap; 6 | 7 | namespace Magma.NetMap.Interop 8 | { 9 | internal static partial class Libc 10 | { 11 | [DllImport("libc", EntryPoint = "read")] 12 | internal unsafe static extern int Read(FileDescriptor fileDescriptor, void* buffer, long size); 13 | 14 | [DllImport("libc", EntryPoint = "write")] 15 | internal unsafe static extern int Write(FileDescriptor fileDescriptor, void* buffer, long size); 16 | 17 | [DllImport("libc", EntryPoint = "open")] 18 | internal static extern FileDescriptor Open([MarshalAs(UnmanagedType.LPStr)] string fileName, OpenFlags flags); 19 | 20 | [DllImport("libc", EntryPoint = "close")] 21 | internal static extern int Close(FileDescriptor fd); 22 | 23 | [DllImport("libc", EntryPoint = "ioctl")] 24 | internal static extern int IOCtl(FileDescriptor descriptor, IOControlCommand request, ref NetMapRequest data); 25 | 26 | [DllImport("libc", EntryPoint = "ioctl")] 27 | internal static extern int IOCtl(FileDescriptor descriptor, IOControlCommand request, IntPtr ptr); 28 | 29 | 30 | internal struct FileDescriptor 31 | { 32 | public int Descriptor; 33 | public bool IsValid => Descriptor > 0; 34 | } 35 | 36 | internal enum IOControlCommand : uint 37 | { 38 | NIOCREGIF = 0xC03C6992, 39 | NIOCTXSYNC = 27028, 40 | NIOCRXSYNC = 27029, 41 | } 42 | 43 | [Flags] 44 | internal enum OpenFlags 45 | { 46 | O_RDONLY = 0x0000, /* open for reading only */ 47 | O_WRONLY = 0x0001, /* open for writing only */ 48 | O_RDWR = 0x0002, /* open for reading and writing */ 49 | O_ACCMODE = 0x0003, /* mask for above modes */ 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Interop/Libc/MMap.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace Magma.NetMap.Interop 7 | { 8 | internal static partial class Libc 9 | { 10 | [DllImport("libc", EntryPoint = "mmap")] 11 | public static extern IntPtr MMap(IntPtr addr, ulong length, MemoryMappedProtections prot, MemoryMappedFlags flags, FileDescriptor fd, ulong offset); 12 | 13 | [DllImport("libc", EntryPoint = "munmap")] 14 | public static extern int MUnmap(IntPtr addr, ulong length); 15 | 16 | [Flags] 17 | public enum MemoryMappedProtections 18 | { 19 | PROT_NONE = 0x0, 20 | PROT_READ = 0x1, 21 | PROT_WRITE = 0x2, 22 | PROT_EXEC = 0x4 23 | } 24 | 25 | [Flags] 26 | public enum MemoryMappedFlags 27 | { 28 | MAP_SHARED = 0x01, 29 | MAP_PRIVATE = 0x02, 30 | MAP_ANONYMOUS = 0x20, 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Interop/Libc/Poll.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace Magma.NetMap.Interop 7 | { 8 | internal static partial class Libc 9 | { 10 | [DllImport("libc", EntryPoint = "poll")] 11 | internal static extern int Poll(ref PollFileDescriptor pollfd, int numberOfFileDescriptors, int timeout); 12 | 13 | [DllImport("libc", EntryPoint = "poll")] 14 | internal unsafe static extern int Poll(PollFileDescriptor* pollfd, int numberOfFileDescriptors, int timeout); 15 | 16 | [StructLayout(LayoutKind.Sequential)] 17 | public struct PollFileDescriptor 18 | { 19 | public FileDescriptor Fd; 20 | public PollEvents Events; 21 | public PollEvents Revents; 22 | } 23 | 24 | [Flags] 25 | public enum PollEvents : short 26 | { 27 | POLLIN = 0x0001, // There is data to read 28 | POLLPRI = 0x0002, // There is urgent data to read 29 | POLLOUT = 0x0004, // Writing now will not block 30 | POLLERR = 0x0008, // Error condition 31 | POLLHUP = 0x0010, // Hung up 32 | POLLNVAL = 0x0020, // Invalid request; fd not open 33 | // XPG4.2 definitions (via _XOPEN_SOURCE) 34 | POLLRDNORM = 0x0040, // Normal data may be read 35 | POLLRDBAND = 0x0080, // Priority data may be read 36 | POLLWRNORM = 0x0100, // Writing now will not block 37 | POLLWRBAND = 0x0200, // Priority data may be written 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Interop/Libc/TimeEval.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace Magma.NetMap.Interop 7 | { 8 | internal static partial class Libc 9 | { 10 | [StructLayout(LayoutKind.Sequential)] 11 | internal struct TimeValue 12 | { 13 | public ulong tv_sec; 14 | public ulong tv_usec; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Interop/Netmap/Open.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | using static Magma.NetMap.Interop.Libc; 6 | 7 | namespace Magma.NetMap.Interop 8 | { 9 | internal static partial class Netmap 10 | { 11 | internal const ushort NETMAP_API = 12; 12 | 13 | public unsafe static FileDescriptor OpenNetMap(string interfaceName, int ringId, NetMapRequestFlags flags, out NetMapRequest returnedRequest) 14 | { 15 | var fd = Open("/dev/netmap", OpenFlags.O_RDWR); 16 | if (!fd.IsValid) ExceptionHelper.ThrowInvalidOperation($"Unable to open the /dev/netmap device {fd}"); 17 | var request = new NetMapRequest 18 | { 19 | nr_cmd = 0, 20 | nr_ringid = (ushort)ringId, 21 | nr_version = NETMAP_API, 22 | nr_flags = flags, 23 | }; 24 | var textbytes = Encoding.ASCII.GetBytes(interfaceName + "\0"); 25 | fixed (void* txtPtr = textbytes) 26 | { 27 | Buffer.MemoryCopy(txtPtr, request.nr_name, textbytes.Length, textbytes.Length); 28 | } 29 | if (IOCtl(fd, IOControlCommand.NIOCREGIF, ref request) != 0) ExceptionHelper.ThrowInvalidOperation("Failed to open an FD for a single ring"); 30 | returnedRequest = request; 31 | return fd; 32 | } 33 | 34 | [StructLayout(LayoutKind.Sequential)] 35 | internal unsafe struct NetMapRequest 36 | { 37 | public fixed byte nr_name[16]; 38 | public uint nr_version; /* API version */ 39 | public uint nr_offset; /* nifp offset in the shared region */ 40 | public uint nr_memsize; /* size of the shared region */ 41 | public uint nr_tx_slots; /* slots in tx rings */ 42 | public uint nr_rx_slots; /* slots in rx rings */ 43 | public ushort nr_tx_rings; /* number of tx rings */ 44 | public ushort nr_rx_rings; /* number of rx rings */ 45 | public ushort nr_ringid; /* ring(s) we care about */ 46 | public NetmapCommand nr_cmd; 47 | public ushort nr_arg1; /* reserve extra rings in NIOCREGIF */ 48 | public ushort nr_arg2; 49 | public uint nr_arg3; /* req. extra buffers in NIOCREGIF */ 50 | public NetMapRequestFlags nr_flags; 51 | public uint spare1; 52 | }; 53 | 54 | internal enum NetmapCommand : ushort 55 | { 56 | NETMAP_BDG_ATTACH = 1, /* attach the NIC */ 57 | NETMAP_BDG_DETACH = 2, /* detach the NIC */ 58 | NETMAP_BDG_REGOPS = 3, /* register bridge callbacks */ 59 | NETMAP_BDG_LIST = 4, /* get bridge's info */ 60 | NETMAP_BDG_VNET_HDR = 5, /* set the port virtio-net-hdr length */ 61 | NETMAP_BDG_NEWIF = 6, /* create a virtual port */ 62 | NETMAP_BDG_DELIF = 7, /* destroy a virtual port */ 63 | NETMAP_PT_HOST_CREATE = 8, /* create ptnetmap kthreads */ 64 | NETMAP_PT_HOST_DELETE = 9,/* delete ptnetmap kthreads */ 65 | NETMAP_BDG_POLLING_ON = 10,/* delete polling kthread */ 66 | NETMAP_BDG_POLLING_OFF = 11,/* delete polling kthread */ 67 | NETMAP_VNET_HDR_GET = 12, /* get the port virtio-net-hdr length */ 68 | } 69 | 70 | [Flags] 71 | internal enum NetMapRequestFlags : uint 72 | { 73 | NR_REG_ALL_NIC = 1, 74 | NR_REG_SW = 2, 75 | NR_REG_NIC_SW = 3, 76 | NR_REG_ONE_NIC = 4, 77 | NR_MONITOR_TX = 0x100, 78 | NR_MONITOR_RX = 0x200, 79 | NR_ZCOPY_MON = 0x400, 80 | NR_EXCLUSIVE = 0x800, 81 | NR_PTNETMAP_HOST = 0x1000, 82 | NR_RX_RINGS_ONLY = 0x2000, 83 | NR_TX_RINGS_ONLY = 0x4000, 84 | NR_ACCEPT_VNET_HDR = 0x8000, 85 | NR_DO_RX_POLL = 0x10000, 86 | NR_NO_TX_POLL = 0x20000, 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Interop/Netmap/Rings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using static Magma.NetMap.Interop.Libc; 4 | 5 | namespace Magma.NetMap.Interop 6 | { 7 | internal static partial class Netmap 8 | { 9 | [StructLayout(LayoutKind.Sequential)] 10 | internal struct NetmapRing 11 | { 12 | public long BuffersOffset; 13 | public uint NumberOfSlotsPerRing; 14 | public uint BufferSize; 15 | public ushort RingId; 16 | public NetmapRingDirection dir; 17 | public int Head; 18 | public int Cursor; 19 | public int Tail; 20 | public uint Flags; 21 | public TimeValue LastSyncTimestamp; 22 | } 23 | 24 | [StructLayout(LayoutKind.Sequential)] 25 | internal struct NetmapSlot 26 | { 27 | public uint buf_idx; /* buffer index */ 28 | public ushort len; /* length for this slot */ 29 | public NetmapSlotFlags flags; /* buf changed, etc. */ 30 | public IntPtr ptr; /* pointer for indirect buffers */ 31 | } 32 | 33 | [Flags] 34 | internal enum NetmapSlotFlags : ushort 35 | { 36 | NS_BUF_CHANGED = 0x0001, 37 | NS_REPORT = 0x0002, 38 | NS_FORWARD = 0x0004, 39 | NS_NO_LEARN = 0x0008, 40 | NS_INDIRECT = 0x0010, 41 | NS_MOREFRAG = 0x0020, 42 | NS_PORT_SHIFT = 8, 43 | NS_PORT_MASK = (0xff << NS_PORT_SHIFT), 44 | } 45 | 46 | internal enum NetmapRingDirection : ushort 47 | { 48 | tx = 1, 49 | rx = 0, 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Interop/RxTxPair.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using static Magma.NetMap.Interop.Libc; 4 | using static Magma.NetMap.Interop.Netmap; 5 | 6 | namespace Magma.NetMap.Interop 7 | { 8 | internal class RxTxPair:IDisposable 9 | { 10 | private FileDescriptor _rxFileDescriptor; 11 | private FileDescriptor _txFileDescriptor; 12 | private readonly int _ringId; 13 | private readonly bool _isHostStack; 14 | 15 | internal unsafe RxTxPair(string interfaceName, int ringId, bool isHostStack) 16 | { 17 | _ringId = ringId; 18 | _isHostStack = isHostStack; 19 | var flags = isHostStack ? NetMapRequestFlags.NR_REG_SW : NetMapRequestFlags.NR_REG_ONE_NIC; 20 | ringId = isHostStack ? 0 : ringId; 21 | 22 | _rxFileDescriptor = OpenNetMap(interfaceName, ringId, flags | NetMapRequestFlags.NR_RX_RINGS_ONLY | NetMapRequestFlags.NR_NO_TX_POLL, out var request); 23 | _txFileDescriptor = OpenNetMap(interfaceName, ringId, flags | NetMapRequestFlags.NR_TX_RINGS_ONLY, out request); 24 | } 25 | 26 | public void WaitForWork() 27 | { 28 | var pfd = new PollFileDescriptor() 29 | { 30 | Events = PollEvents.POLLIN, 31 | Fd = _rxFileDescriptor, 32 | }; 33 | var result = Poll(ref pfd, 1, -1); 34 | if (result < 0) ExceptionHelper.ThrowInvalidOperation("Error on poll"); 35 | } 36 | 37 | public unsafe void ForceFlush() => IOCtl(_txFileDescriptor, IOControlCommand.NIOCTXSYNC, IntPtr.Zero); 38 | 39 | public void Dispose() 40 | { 41 | if(_rxFileDescriptor.IsValid) 42 | { 43 | Close(_rxFileDescriptor); 44 | _rxFileDescriptor = new FileDescriptor(); 45 | } 46 | if(_txFileDescriptor.IsValid) 47 | { 48 | Close(_txFileDescriptor); 49 | _txFileDescriptor = new FileDescriptor(); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Interop/global.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.Runtime.CompilerServices; 3 | 4 | [assembly:InternalsVisibleTo("Magma.NetMap.Facts")] 5 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Interop/netmap_if.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace Magma.NetMap.Interop 7 | { 8 | [StructLayout(LayoutKind.Sequential)] 9 | public unsafe struct NetMapInterface 10 | { 11 | public fixed byte Name[16]; 12 | public uint Version; 13 | public uint Flags; 14 | public int NumberOfTXRings; 15 | public int NumberOfRXRings; 16 | public uint ni_bufs_head; 17 | public uint spare1; 18 | public uint spare2; 19 | public uint spare3; 20 | public uint spare4; 21 | public uint spare5; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Magma.NetMap/Magma.NetMap.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | true 6 | latest 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Magma.NetMap/NetMapTransport.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Threading.Tasks; 5 | using Magma.NetMap.Internal; 6 | using Magma.Network.Abstractions; 7 | using Magma.Transport.Tcp; 8 | using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; 9 | 10 | namespace Magma.NetMap 11 | { 12 | public class NetMapTransport : ITransport 13 | { 14 | private NetMapPort> _port; 15 | private IPEndPoint _endpoint; 16 | private string _interfaceName; 17 | private IConnectionDispatcher _connectionDispatcher; 18 | private List> _receivers = new List>(); 19 | 20 | public NetMapTransport(IPEndPoint ipEndpoint, string interfaceName, IConnectionDispatcher dispatcher) 21 | { 22 | _endpoint = ipEndpoint ?? throw new ArgumentNullException(nameof(ipEndpoint)); 23 | _interfaceName = interfaceName ?? throw new ArgumentNullException(nameof(interfaceName)); 24 | _connectionDispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); 25 | Console.WriteLine($"Transport started with ip endpoint {ipEndpoint} on interface name {interfaceName}"); 26 | } 27 | 28 | public Task BindAsync() 29 | { 30 | _port = new NetMapPort>(_interfaceName, CreateReceiver); 31 | _port.Open(); 32 | Console.WriteLine($"Bind completed and netmap port open"); 33 | return Task.CompletedTask; 34 | } 35 | 36 | private TcpTransportReceiver CreateReceiver(NetMapTransmitRing transmitRing) 37 | { 38 | var receiver = new TcpTransportReceiver(_endpoint, transmitRing, _connectionDispatcher); 39 | _receivers.Add(receiver); 40 | Console.WriteLine("Creating receiver"); 41 | return receiver; 42 | } 43 | 44 | public Task StopAsync() 45 | { 46 | _port.Dispose(); 47 | _port = null; 48 | return Task.CompletedTask; 49 | } 50 | 51 | public Task UnbindAsync() => Task.CompletedTask; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Magma.NetMap/NetMapTransportFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; 6 | using Microsoft.Extensions.Logging; 7 | using Microsoft.Extensions.Options; 8 | 9 | namespace Magma.NetMap 10 | { 11 | public class NetMapTransportFactory : ITransportFactory 12 | { 13 | private readonly NetMapTransportOptions _options; 14 | private readonly IApplicationLifetime _appLifetime; 15 | 16 | public NetMapTransportFactory(IOptions options, IApplicationLifetime applicationLifetime) 17 | { 18 | _options = options?.Value ?? throw new ArgumentNullException(nameof(options)); 19 | _appLifetime = applicationLifetime ?? throw new ArgumentNullException(nameof(applicationLifetime)); 20 | } 21 | 22 | public ITransport Create(IEndPointInformation endPointInformation, IConnectionDispatcher dispatcher) 23 | { 24 | endPointInformation = endPointInformation ?? throw new ArgumentNullException(nameof(endPointInformation)); 25 | dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); 26 | 27 | var transport = new NetMapTransport(endPointInformation.IPEndPoint, _options.InterfaceName, dispatcher); 28 | return transport; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Magma.NetMap/NetMapTransportOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Magma.NetMap 6 | { 7 | public class NetMapTransportOptions 8 | { 9 | public string InterfaceName { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Magma.NetMap/WebHostBuilderNetMapExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Magma.NetMap; 3 | using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace Microsoft.AspNetCore.Hosting 7 | { 8 | public static class WebHostBuilderNetMapExtensions 9 | { 10 | /// 11 | /// Specify NetMap as the transport to be used by Kestrel. 12 | /// 13 | /// 14 | /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder to configure. 15 | /// 16 | /// 17 | /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder. 18 | /// 19 | public static IWebHostBuilder UseNetMap(this IWebHostBuilder hostBuilder) 20 | { 21 | return hostBuilder.ConfigureServices(services => 22 | { 23 | services.AddSingleton(); 24 | }); 25 | } 26 | 27 | /// 28 | /// Specify NetMap as the transport to be used by Kestrel. 29 | /// 30 | /// 31 | /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder to configure. 32 | /// 33 | /// 34 | /// A callback to configure NetMap options. 35 | /// 36 | /// 37 | /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder. 38 | /// 39 | public static IWebHostBuilder UseNetMap(this IWebHostBuilder hostBuilder, Action configureOptions) 40 | { 41 | return hostBuilder.UseNetMap().ConfigureServices(services => 42 | { 43 | services.Configure(configureOptions); 44 | }); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Magma.Network.Abstractions/IPacketTransmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace Magma.Network.Abstractions 7 | { 8 | public interface IPacketTransmitter 9 | { 10 | bool TryGetNextBuffer(out Memory buffer); 11 | Task SendBuffer(ReadOnlyMemory buffer); 12 | uint RandomSequenceNumber(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Magma.Network.Abstractions/Magma.Network.Abstractions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/Magma.Network.Abstractions/PacketReceiver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | 4 | namespace Magma.Network.Abstractions 5 | { 6 | public interface IPacketReceiver 7 | { 8 | T TryConsume(T input) where T : IMemoryOwner; 9 | void FlushPendingAcks(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Magma.Network/DefaultPacketReceiver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using Magma.Network.Abstractions; 4 | using Magma.Network.Header; 5 | 6 | namespace Magma.Network 7 | { 8 | public struct DefaultPacketReceiver : IPacketReceiver 9 | { 10 | private PacketReceiver _packetReceiver; 11 | 12 | public static DefaultPacketReceiver CreateDefault() => 13 | new DefaultPacketReceiver() 14 | { 15 | _packetReceiver = PacketReceiver.CreateDefault() 16 | }; 17 | 18 | public void FlushPendingAcks() 19 | { 20 | throw new NotImplementedException(); 21 | } 22 | 23 | public T TryConsume(T input) where T : IMemoryOwner => _packetReceiver.TryConsume(input); 24 | } 25 | 26 | internal class PacketReceiver : IPacketReceiver 27 | { 28 | private readonly static ConsumeInternetLayer _ipv4Consumer = (in Ethernet ethernetFrame, ReadOnlySpan input) 29 | => TryConsumeIPv4(in ethernetFrame, input); 30 | private readonly static ConsumeInternetLayer _ipv6Consumer = (in Ethernet ethernetFrame, ReadOnlySpan input) 31 | => TryConsumeIPv6(in ethernetFrame, input); 32 | private readonly static ConsumeInternetLayer _arpConsumer = (in Ethernet ethernetFrame, ReadOnlySpan input) 33 | => TryConsumeArp(in ethernetFrame, input); 34 | 35 | public ConsumeInternetLayer IPv4Consumer { get; set; } 36 | public ConsumeInternetLayer IPv6Consumer { get; set; } 37 | public ConsumeInternetLayer ArpConsumer { get; set; } 38 | 39 | public PacketReceiver() { } 40 | 41 | public static PacketReceiver CreateDefault() => new PacketReceiver() 42 | { 43 | IPv4Consumer = _ipv4Consumer, 44 | IPv6Consumer = _ipv6Consumer, 45 | ArpConsumer = _arpConsumer 46 | }; 47 | 48 | public T TryConsume(T owner) where T : IMemoryOwner 49 | { 50 | var input = owner.Memory.Span; 51 | var result = false; 52 | if (Ethernet.TryConsume(input, out var ethernetFrame, out var data)) 53 | { 54 | switch (ethernetFrame.Ethertype) 55 | { 56 | case EtherType.IPv4: 57 | result = IPv4Consumer?.Invoke(in ethernetFrame, data) ?? false; 58 | break; 59 | case EtherType.IPv6: 60 | result = IPv6Consumer?.Invoke(in ethernetFrame, data) ?? false; 61 | break; 62 | case EtherType.Arp: 63 | result = ArpConsumer?.Invoke(in ethernetFrame, data) ?? false; 64 | break; 65 | default: 66 | // Unsupported protocol pass to host OS networking 67 | result = false; 68 | break; 69 | } 70 | } 71 | else 72 | { 73 | // Couldn't parse, consume the invalid packet 74 | result = true; 75 | } 76 | if(result) 77 | { 78 | owner.Dispose(); 79 | return default; 80 | } 81 | return owner; 82 | } 83 | 84 | private static bool TryConsumeIPv4(in Ethernet ethernetFrame, ReadOnlySpan input) 85 | { 86 | return false; 87 | } 88 | 89 | private static bool TryConsumeIPv6(in Ethernet ethernetFrame, ReadOnlySpan input) 90 | { 91 | return false; 92 | } 93 | 94 | private static bool TryConsumeArp(in Ethernet ethernetFrame, ReadOnlySpan input) 95 | { 96 | return false; 97 | } 98 | 99 | public void FlushPendingAcks() 100 | { 101 | throw new NotImplementedException(); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Magma.Network/Delegates.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Magma.Network.Header; 3 | 4 | namespace Magma.Network 5 | { 6 | public delegate bool ConsumeInternetLayer(in Ethernet ethernetFrame, ReadOnlySpan input); 7 | public delegate bool IPv4ConsumeTransportLayer(in Ethernet ethernetFrame, in IPv4 ipv4, ReadOnlySpan input); 8 | public delegate bool IPv6ConsumeTransportLayer(in Ethernet ethernetFrame, in IPv6 ipv6, ReadOnlySpan input); 9 | } 10 | -------------------------------------------------------------------------------- /src/Magma.Network/Magma.Network.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | latest 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Magma.PCap/FileHeader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace Magma.PCap 7 | { 8 | [StructLayout(LayoutKind.Sequential, Pack =1)] 9 | public struct FileHeader 10 | { 11 | private uint _magicNumber; 12 | private ushort _versionMajor; 13 | private ushort _versionMinor; 14 | private uint _timezone; 15 | private uint _sigFlags; 16 | public uint SnapLength; 17 | private NetworkKind _network; 18 | 19 | public static FileHeader Create() 20 | { 21 | var fileHeader = new FileHeader() 22 | { 23 | _magicNumber = 0xa1b2c3d4, 24 | _versionMajor = 2, 25 | _versionMinor = 4, 26 | _timezone = 0, 27 | _sigFlags = 0, 28 | SnapLength = 2000, 29 | _network = NetworkKind.LINKTYPE_ETHERNET, 30 | }; 31 | return fileHeader; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Magma.PCap/Magma.PCap.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Magma.PCap/NetworkKind.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Magma.PCap 6 | { 7 | public enum NetworkKind : uint 8 | { 9 | LINKTYPE_NULL = 0, 10 | LINKTYPE_ETHERNET = 1, 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Magma.PCap/PcapFileWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace Magma.PCap 8 | { 9 | public class PCapFileWriter:IDisposable 10 | { 11 | private FileStream _fileStream; 12 | private object _lock = new object(); 13 | private int _maxSize; 14 | 15 | public unsafe PCapFileWriter(string fileName) 16 | { 17 | _fileStream = new FileStream(fileName, FileMode.Create); 18 | var header = FileHeader.Create(); 19 | _maxSize = (int)header.SnapLength; 20 | var span = new Span(&header, Unsafe.SizeOf()); 21 | _fileStream.Write(span); 22 | } 23 | 24 | public void Dispose() => _fileStream.Dispose(); 25 | 26 | public unsafe void WritePacket(Span packet) 27 | { 28 | var time = (DateTime.UtcNow - new DateTime(1970, 1, 1)); 29 | var header = new RecordHeader() 30 | { 31 | incl_len = packet.Length, 32 | orig_len = packet.Length, 33 | ts_sec = (uint)time.TotalSeconds, 34 | ts_usec = (uint)time.Milliseconds, 35 | }; 36 | var headerSpan = new Span(&header, Unsafe.SizeOf()); 37 | lock(_lock) 38 | { 39 | _fileStream.Write(headerSpan); 40 | _fileStream.Write(packet); 41 | _fileStream.Flush(); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Magma.PCap/RecordHeader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace Magma.PCap 7 | { 8 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 9 | public struct RecordHeader 10 | { 11 | public uint ts_sec; /* timestamp seconds */ 12 | public uint ts_usec; /* timestamp microseconds */ 13 | public int incl_len; /* number of octets of packet saved in file */ 14 | public int orig_len; /* actual length of packet */ 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Magma.Transport.Tcp/Header/TcpFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Magma.Transport.Tcp.Header 4 | { 5 | [Flags] 6 | public enum TcpFlags : int 7 | { 8 | None = 0, 9 | 10 | NS = 0b1_0000_0000, 11 | CWR = 0b0_1000_0000, 12 | ECE = 0b0_0100_0000, 13 | URG = 0b0_0010_0000, 14 | ACK = 0b0_0001_0000, 15 | PSH = 0b0_0000_1000, 16 | RST = 0b0_0000_0100, 17 | SYN = 0b0_0000_0010, 18 | FIN = 0b0_0000_0001 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Magma.Transport.Tcp/Header/TcpHeaderWithOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | 7 | namespace Magma.Transport.Tcp.Header 8 | { 9 | public struct TcpHeaderWithOptions 10 | { 11 | public Network.Header.Tcp Header; 12 | private byte _windowScale; 13 | private ushort _maximumSegmentSize; 14 | private bool _sackPermitted; 15 | private uint _timeStamp; 16 | private uint _timeStampEchoReply; 17 | 18 | public bool SackPermitted => _sackPermitted; 19 | public ushort MaximumSegmentSize => _maximumSegmentSize; 20 | public byte WindowScale => _windowScale; 21 | public uint TimeStamp => _timeStamp; 22 | 23 | public static bool TryConsume(ReadOnlySpan input, out TcpHeaderWithOptions headerWithOps, out ReadOnlySpan data) 24 | { 25 | if (!Network.Header.Tcp.TryConsume(input, out var tcpHeader, out var options, out data)) 26 | { 27 | headerWithOps = default; 28 | return false; 29 | } 30 | 31 | headerWithOps = new TcpHeaderWithOptions() { Header = tcpHeader }; 32 | 33 | var exit = false; 34 | var originalOptions = options; 35 | try 36 | { 37 | while (options.Length > 0 && !exit) 38 | { 39 | var optionKind = (TcpOptionKind)options[0]; 40 | switch (optionKind) 41 | { 42 | case TcpOptionKind.WindowScale: 43 | headerWithOps._windowScale = options[2]; 44 | options = options.Slice(3); 45 | break; 46 | case TcpOptionKind.MaximumSegmentSize: 47 | headerWithOps._maximumSegmentSize = (ushort)(options[2] << 8 | options[3]); 48 | options = options.Slice(4); 49 | break; 50 | case TcpOptionKind.NoOp: 51 | options = options.Slice(1); 52 | break; 53 | case TcpOptionKind.SackPermitted: 54 | headerWithOps._sackPermitted = true; 55 | options = options.Slice(2); 56 | break; 57 | case TcpOptionKind.Timestamps: 58 | headerWithOps._timeStamp = System.Buffers.Binary.BinaryPrimitives.ReadUInt32BigEndian(options.Slice(2)); 59 | headerWithOps._timeStampEchoReply = System.Buffers.Binary.BinaryPrimitives.ReadUInt32BigEndian(options.Slice(6)); 60 | options = options.Slice(10); 61 | break; 62 | case TcpOptionKind.EndOfOptions: 63 | exit = true; 64 | break; 65 | default: 66 | exit = true; 67 | //Console.WriteLine($"Unknown option kind {optionKind}"); 68 | options = options.Slice(options[1]); 69 | break; 70 | } 71 | } 72 | } 73 | catch 74 | { 75 | //Console.WriteLine($"Failed to parse options data was {BitConverter.ToString(input.ToArray())}"); 76 | } 77 | return true; 78 | } 79 | 80 | public static int SizeOfSynAckHeader = Unsafe.SizeOf() + Unsafe.SizeOf() + Unsafe.SizeOf() + Unsafe.SizeOf(); 81 | public static int SizeOfStandardHeader = Unsafe.SizeOf(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Magma.Transport.Tcp/Header/TcpOptionKind.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Magma.Transport.Tcp.Header 6 | { 7 | public enum TcpOptionKind : byte 8 | { 9 | EndOfOptions = 0, 10 | NoOp = 1, 11 | MaximumSegmentSize = 2, 12 | WindowScale = 3, 13 | SackPermitted = 4, 14 | Sack = 5, 15 | Echo = 6, 16 | EchoReply = 7, 17 | Timestamps = 8, 18 | PartialOrderConnectionPermitted = 9, 19 | PartialOrderServiceProfile = 10, 20 | CC = 11, 21 | CCNew = 12, 22 | CCEcho = 13, 23 | TcpAlternateChecksumRequested = 14, 24 | TcpAlternateChecksumData = 15, 25 | Skeeter = 16, 26 | Bubba = 17, 27 | TrailerChecksumOption = 18, 28 | MD5ChecksumOption = 19, 29 | ScpsCapabilities = 20, 30 | SelectiveNegativeAcknowledgements = 21, 31 | RecordBoundaries = 22, 32 | CorruptionExperinced = 23, 33 | Snap = 24, 34 | Unassigned = 25, 35 | TcpCompressionFilter = 26, 36 | QuickStartResponse = 27, 37 | UserTimeoutOption = 28, 38 | TcpAuthenticationOption = 29, 39 | MultiPathTcp = 30, 40 | TcpFastOpenCookie = 34, 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Magma.Transport.Tcp/Header/TcpOptionMaxSegmentSize.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace Magma.Transport.Tcp.Header 7 | { 8 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 9 | public struct TcpOptionMaxSegmentSize 10 | { 11 | private TcpOptionKind _optionKind; 12 | private byte _length; 13 | private ushort _size; 14 | 15 | public TcpOptionMaxSegmentSize(ushort size) 16 | { 17 | _optionKind = TcpOptionKind.MaximumSegmentSize; 18 | _length = 4; 19 | _size = (ushort)System.Net.IPAddress.HostToNetworkOrder((short)size); 20 | } 21 | 22 | public ushort Size => (ushort)System.Net.IPAddress.NetworkToHostOrder((short)_size); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Magma.Transport.Tcp/Header/TcpOptionTimestamp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace Magma.Transport.Tcp.Header 7 | { 8 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 9 | public struct TcpOptionTimestamp 10 | { 11 | private ushort _padding; 12 | private TcpOptionKind _optionKind; 13 | private byte _size; 14 | private uint _timestampValue; 15 | private uint _timestampEchoReply; 16 | 17 | public TcpOptionTimestamp(uint timestampValue, uint timestampEchoReply) 18 | { 19 | _padding = 0x0101; 20 | _optionKind = TcpOptionKind.Timestamps; 21 | _size = 10; 22 | _timestampValue = (uint)System.Net.IPAddress.HostToNetworkOrder((int)timestampValue); 23 | _timestampEchoReply = (uint)System.Net.IPAddress.HostToNetworkOrder((int)timestampEchoReply); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Magma.Transport.Tcp/Header/TcpOptionWindowScale.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace Magma.Transport.Tcp.Header 7 | { 8 | [StructLayout(LayoutKind.Sequential, Pack =1)] 9 | public struct TcpOptionWindowScale 10 | { 11 | private byte _padding; 12 | private TcpOptionKind _optionKind; 13 | private byte _size; 14 | private byte _scale; 15 | 16 | public TcpOptionWindowScale(byte scale) 17 | { 18 | _padding = 1; 19 | _optionKind = TcpOptionKind.WindowScale; 20 | _size = 3; 21 | _scale = scale; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Magma.Transport.Tcp/Header/TcpV4PseudoHeader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | using Magma.Internet.Ip; 6 | using static Magma.Network.IPAddress; 7 | 8 | namespace Magma.Transport.Tcp.Header 9 | { 10 | [StructLayout(LayoutKind.Sequential, Pack =1)] 11 | public struct TcpV4PseudoHeader 12 | { 13 | public V4Address Source; 14 | public V4Address Destination; 15 | public byte Reserved; 16 | public ProtocolNumber ProtocolNumber; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Magma.Transport.Tcp/Internal/ISocketsTrace.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace Magma.Transport.Tcp.Internal 5 | { 6 | public interface ISocketsTrace : ILogger 7 | { 8 | void ConnectionReadFin(string connectionId); 9 | 10 | void ConnectionWriteFin(string connectionId); 11 | 12 | void ConnectionError(string connectionId, Exception ex); 13 | 14 | void ConnectionReset(string connectionId); 15 | 16 | void ConnectionPause(string connectionId); 17 | 18 | void ConnectionResume(string connectionId); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Magma.Transport.Tcp/Internal/SocketAwaitable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO.Pipelines; 4 | using System.Net.Sockets; 5 | using System.Runtime.CompilerServices; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace Magma.Transport.Tcp.Internal 10 | { 11 | public class SocketAwaitable : ICriticalNotifyCompletion 12 | { 13 | private static readonly Action _callbackCompleted = () => { }; 14 | 15 | private readonly PipeScheduler _ioScheduler; 16 | 17 | private Action _callback; 18 | private int _bytesTransferred; 19 | private SocketError _error; 20 | 21 | public SocketAwaitable(PipeScheduler ioScheduler) 22 | { 23 | _ioScheduler = ioScheduler; 24 | } 25 | 26 | public SocketAwaitable GetAwaiter() => this; 27 | public bool IsCompleted => ReferenceEquals(_callback, _callbackCompleted); 28 | 29 | public int GetResult() 30 | { 31 | Debug.Assert(ReferenceEquals(_callback, _callbackCompleted)); 32 | 33 | _callback = null; 34 | 35 | if (_error != SocketError.Success) 36 | { 37 | throw new SocketException((int)_error); 38 | } 39 | 40 | return _bytesTransferred; 41 | } 42 | 43 | public void OnCompleted(Action continuation) 44 | { 45 | if (ReferenceEquals(_callback, _callbackCompleted) || 46 | ReferenceEquals(Interlocked.CompareExchange(ref _callback, continuation, null), _callbackCompleted)) 47 | { 48 | Task.Run(continuation); 49 | } 50 | } 51 | 52 | public void UnsafeOnCompleted(Action continuation) => OnCompleted(continuation); 53 | 54 | public void Complete(int bytesTransferred, SocketError socketError) 55 | { 56 | _error = socketError; 57 | _bytesTransferred = bytesTransferred; 58 | var continuation = Interlocked.Exchange(ref _callback, _callbackCompleted); 59 | 60 | if (continuation != null) 61 | { 62 | _ioScheduler.Schedule(state => ((Action)state)(), continuation); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Magma.Transport.Tcp/Internal/SocketStates.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.Net; 3 | using System.Threading.Tasks; 4 | 5 | namespace Magma.Transport.Tcp.SocketStates 6 | { 7 | // TCP Connection State Diagram 8 | // 9 | // +---------+ ---------\ active OPEN 10 | // | CLOSED | \ ----------- 11 | // Timeout /-------------->+---------+<---------\ \ create TCB 12 | // ------- / | ^ \ \ snd SYN 13 | // snd RST / passive OPEN | | CLOSE \ \ 14 | // / ------------ | | ---------- \ \ 15 | // / create TCB | | delete TCB \ \ 16 | // / V | \ \ 17 | // | +---------+ CLOSE | \ 18 | // | rcv RST /-------------->| LISTEN | ---------- | | 19 | // | / +---------+ delete TCB | | 20 | // | / rcv SYN | | SEND | | 21 | // | | ----------- | | ------- | V 22 | // +---------+ snd SYN,ACK / \ snd SYN +---------+ 23 | // | |<----------------- ------------------>| | 24 | // | SYN | rcv SYN | SYN | 25 | // | RCVD |<-----------------------------------------------| SENT | 26 | // | | snd ACK | | 27 | // | |------------------ -------------------| | 28 | // +---------+ rcv ACK of SYN \ / rcv SYN,ACK +---------+ 29 | // | -------------- | | ----------- 30 | // | x | | snd ACK 31 | // | V V 32 | // | CLOSE +---------+ 33 | // | ------- | ESTAB | 34 | // | snd FIN +---------+ 35 | // | CLOSE | | rcv FIN 36 | // V ------- | | ------- 37 | // +---------+ snd FIN / \ snd ACK +---------+ 38 | // | FIN |<----------------- ------------------>| CLOSE | 39 | // | WAIT-1 |------------------ | WAIT | 40 | // +---------+ rcv FIN \ +---------+ 41 | // | rcv ACK of FIN ------- | CLOSE | 42 | // | -------------- snd ACK | ------- | 43 | // V x V snd FIN V 44 | // +---------+ +---------+ +---------+ 45 | // |FINWAIT-2| | CLOSING | | LAST-ACK| 46 | // +---------+ +---------+ +---------+ 47 | // | rcv ACK of FIN | rcv ACK of FIN | 48 | // | rcv FIN -------------- | Timeout=2MSL -------------- | 49 | // | ------- x V ------------ x V 50 | // \ snd ACK +---------+delete TCB +---------+ 51 | // ------------------------>|TIME WAIT|------------------>| CLOSED | 52 | // +---------+ +---------+ 53 | 54 | 55 | internal struct ResponderClosed 56 | { 57 | public ValueTask ListenAsync() => default; 58 | } 59 | 60 | internal struct ResponderListening 61 | { 62 | public ValueTask WaitSynAsync() => default; 63 | } 64 | internal struct ResponderSynReceived 65 | { 66 | public ValueTask SendRstAsync() => default; 67 | public ValueTask SendSynAckAsync() => default; 68 | } 69 | internal struct ResponderEstablished 70 | { 71 | public IPEndPoint LocalEndPoint { get; } 72 | public IPEndPoint RemoteEndPoint { get; } 73 | public ValueTask WaitCloseAsync() => default; 74 | } 75 | internal struct ResponderCloseWait 76 | { 77 | public ValueTask SendFinAsync() => default; 78 | } 79 | internal struct ResponderLastAck 80 | { 81 | public ValueTask WaitFinAsync() => default; 82 | } 83 | 84 | internal struct InitatorClosed { } 85 | internal struct InitatorSynSent { } 86 | internal struct InitatorEstablished 87 | { 88 | public IPEndPoint LocalEndPoint { get; } 89 | public IPEndPoint RemoteEndPoint { get; } 90 | } 91 | internal struct InitatorFinWait1 { } 92 | internal struct InitatorFinWait2 { } 93 | internal struct InitatorClosing { } 94 | internal struct InitatorTimeWait { } 95 | } 96 | -------------------------------------------------------------------------------- /src/Magma.Transport.Tcp/Internal/SocketsTrace.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace Magma.Transport.Tcp.Internal 5 | { 6 | public class SocketsTrace : ISocketsTrace 7 | { 8 | // ConnectionRead: Reserved: 3 9 | 10 | private static readonly Action _connectionPause = 11 | LoggerMessage.Define(LogLevel.Debug, new EventId(4, nameof(ConnectionPause)), @"Connection id ""{ConnectionId}"" paused."); 12 | 13 | private static readonly Action _connectionResume = 14 | LoggerMessage.Define(LogLevel.Debug, new EventId(5, nameof(ConnectionResume)), @"Connection id ""{ConnectionId}"" resumed."); 15 | 16 | private static readonly Action _connectionReadFin = 17 | LoggerMessage.Define(LogLevel.Debug, new EventId(6, nameof(ConnectionReadFin)), @"Connection id ""{ConnectionId}"" received FIN."); 18 | 19 | private static readonly Action _connectionWriteFin = 20 | LoggerMessage.Define(LogLevel.Debug, new EventId(7, nameof(ConnectionWriteFin)), @"Connection id ""{ConnectionId}"" sending FIN."); 21 | 22 | private static readonly Action _connectionError = 23 | LoggerMessage.Define(LogLevel.Information, new EventId(14, nameof(ConnectionError)), @"Connection id ""{ConnectionId}"" communication error."); 24 | 25 | private static readonly Action _connectionReset = 26 | LoggerMessage.Define(LogLevel.Debug, new EventId(19, nameof(ConnectionReset)), @"Connection id ""{ConnectionId}"" reset."); 27 | 28 | private readonly ILogger _logger; 29 | 30 | public SocketsTrace(ILogger logger) 31 | { 32 | _logger = logger; 33 | } 34 | 35 | public void ConnectionRead(string connectionId, int count) 36 | { 37 | // Don't log for now since this could be *too* verbose. 38 | // Reserved: Event ID 3 39 | } 40 | 41 | public void ConnectionReadFin(string connectionId) 42 | { 43 | _connectionReadFin(_logger, connectionId, null); 44 | } 45 | 46 | public void ConnectionWriteFin(string connectionId) 47 | { 48 | _connectionWriteFin(_logger, connectionId, null); 49 | } 50 | 51 | public void ConnectionWrite(string connectionId, int count) 52 | { 53 | // Don't log for now since this could be *too* verbose. 54 | // Reserved: Event ID 11 55 | } 56 | 57 | public void ConnectionWriteCallback(string connectionId, int status) 58 | { 59 | // Don't log for now since this could be *too* verbose. 60 | // Reserved: Event ID 12 61 | } 62 | 63 | public void ConnectionError(string connectionId, Exception ex) 64 | { 65 | _connectionError(_logger, connectionId, ex); 66 | } 67 | 68 | public void ConnectionReset(string connectionId) 69 | { 70 | _connectionReset(_logger, connectionId, null); 71 | } 72 | 73 | public void ConnectionPause(string connectionId) 74 | { 75 | _connectionPause(_logger, connectionId, null); 76 | } 77 | 78 | public void ConnectionResume(string connectionId) 79 | { 80 | _connectionResume(_logger, connectionId, null); 81 | } 82 | 83 | public IDisposable BeginScope(TState state) => _logger.BeginScope(state); 84 | 85 | public bool IsEnabled(LogLevel logLevel) => _logger.IsEnabled(logLevel); 86 | 87 | public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) 88 | => _logger.Log(logLevel, eventId, state, exception, formatter); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Magma.Transport.Tcp/Magma.Transport.Tcp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;netcoreapp2.1 5 | latest 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Magma.Transport.Tcp/Properties/SocketsStrings.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace Magma.Transport.Tcp 3 | { 4 | using System.Globalization; 5 | using System.Reflection; 6 | using System.Resources; 7 | 8 | internal static class SocketsStrings 9 | { 10 | private static readonly ResourceManager _resourceManager 11 | = new ResourceManager("Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketsStrings", typeof(SocketsStrings).GetTypeInfo().Assembly); 12 | 13 | /// 14 | /// Only ListenType.IPEndPoint is supported. 15 | /// 16 | internal static string OnlyIPEndPointsSupported 17 | { 18 | get => GetString("OnlyIPEndPointsSupported"); 19 | } 20 | 21 | /// 22 | /// Only ListenType.IPEndPoint is supported. 23 | /// 24 | internal static string FormatOnlyIPEndPointsSupported() 25 | => GetString("OnlyIPEndPointsSupported"); 26 | 27 | /// 28 | /// Transport is already bound. 29 | /// 30 | internal static string TransportAlreadyBound 31 | { 32 | get => GetString("TransportAlreadyBound"); 33 | } 34 | 35 | /// 36 | /// Transport is already bound. 37 | /// 38 | internal static string FormatTransportAlreadyBound() 39 | => GetString("TransportAlreadyBound"); 40 | 41 | private static string GetString(string name, params string[] formatterNames) 42 | { 43 | var value = _resourceManager.GetString(name); 44 | 45 | System.Diagnostics.Debug.Assert(value != null); 46 | 47 | if (formatterNames != null) 48 | { 49 | for (var i = 0; i < formatterNames.Length; i++) 50 | { 51 | value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); 52 | } 53 | } 54 | 55 | return value; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Magma.Transport.Tcp/SocketsStrings.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Only ListenType.IPEndPoint is supported. 122 | 123 | 124 | Transport is already bound. 125 | 126 | -------------------------------------------------------------------------------- /src/Magma.Transport.Tcp/TcpConnectionState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Magma.Transport.Tcp 6 | { 7 | public enum TcpConnectionState 8 | { 9 | Closed, 10 | Listen, 11 | Syn_Rcvd, 12 | Syn_Sent, 13 | Established, 14 | Fin_Wait_1, 15 | Fin_Wait_2, 16 | Closing, 17 | Time_Wait, 18 | Close_Wait, 19 | Last_Ack, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Magma.Transport.Tcp/TcpTransportReceiver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Magma.Network.Abstractions; 5 | using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; 6 | using static Magma.Network.IPAddress; 7 | using System.Net; 8 | using System.Runtime.CompilerServices; 9 | using System.Buffers; 10 | using Magma.Transport.Tcp.Header; 11 | using Magma.Network.Header; 12 | 13 | namespace Magma.Transport.Tcp 14 | { 15 | public class TcpTransportReceiver : IPacketReceiver where TTransmitter : IPacketTransmitter 16 | { 17 | private TTransmitter _transmitRing; 18 | private IConnectionDispatcher _connectionDispatcher; 19 | private IPEndPoint _ipEndPoint; 20 | private V4Address _address; 21 | private bool _isAny; 22 | private ushort _port; 23 | private Dictionary<(V4Address address, ushort port), TcpConnection> _connections = new Dictionary<(V4Address address, ushort port), TcpConnection>(); 24 | private Random _randomSequenceNumber = new Random(); 25 | private TcpConnection _lastConnectionSentTo; 26 | 27 | public TcpTransportReceiver(IPEndPoint ipEndPoint, TTransmitter transmitRing, IConnectionDispatcher connectionDispatcher) 28 | { 29 | _ipEndPoint = ipEndPoint; 30 | _transmitRing = transmitRing; 31 | _connectionDispatcher = connectionDispatcher; 32 | var bytes = _ipEndPoint.Address.GetAddressBytes(); 33 | _address = Unsafe.As(ref bytes[0]); 34 | _port = (ushort)_ipEndPoint.Port; 35 | _isAny = _ipEndPoint.Address == IPAddress.Any; 36 | } 37 | 38 | public TTransmitter Transmitter => _transmitRing; 39 | 40 | public void FlushPendingAcks() => _lastConnectionSentTo?.SendAckIfRequired(); 41 | 42 | public uint RandomSeqeunceNumber() => (uint)_randomSequenceNumber.Next(); 43 | 44 | public T TryConsume(T input) where T : IMemoryOwner 45 | { 46 | try 47 | { 48 | var span = input.Memory.Span; 49 | if (!Ethernet.TryConsume(span, out var etherHeader, out var data)) return input; 50 | if (!IPv4.TryConsume(data, out var ipHeader, out data, false)) return input; 51 | 52 | // Now we will check the IP Checksum because we actually care 53 | if (ipHeader.Protocol != Internet.Ip.ProtocolNumber.Tcp 54 | || (!_isAny && ipHeader.DestinationAddress != _address) 55 | || !ipHeader.IsChecksumValid()) return input; 56 | 57 | // So we have TCP lets parse out the header 58 | if (!TcpHeaderWithOptions.TryConsume(data, out var tcp, out data) 59 | || tcp.Header.DestinationPort != _port) return input; 60 | try 61 | { 62 | // okay we now have some data we care about all the rest has been ditched to the host rings 63 | if (!_connections.TryGetValue((ipHeader.SourceAddress, tcp.Header.SourcePort), out var connection)) 64 | { 65 | _lastConnectionSentTo?.SendAckIfRequired(); 66 | // We need to create a connection because we don't know this one but only if the packet is a syn 67 | // otherwise this is garbage and we should just swallow it 68 | if (!tcp.Header.SYN) 69 | { 70 | return default; 71 | } 72 | 73 | // So looks like we need to create a connection then 74 | connection = new TcpConnection(etherHeader, ipHeader, tcp.Header, Transmitter, _connectionDispatcher); 75 | _connections[(ipHeader.SourceAddress, tcp.Header.SourcePort)] = connection; 76 | } 77 | else if (connection != _lastConnectionSentTo) 78 | { 79 | _lastConnectionSentTo.SendAckIfRequired(); 80 | } 81 | 82 | connection.ProcessPacket(tcp, data); 83 | _lastConnectionSentTo = connection; 84 | return default; 85 | } 86 | finally 87 | { 88 | input.Dispose(); 89 | } 90 | } 91 | catch (Exception ex) 92 | { 93 | Console.WriteLine($"Error processing packet!!! thread will die exception was {ex}"); 94 | return input; 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Magma.Transport.Udp/Header/Udp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Magma.Network.Header 4 | { 5 | public struct Udp 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Magma.Transport.Udp/Magma.Transport.Udp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | latest 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/Magma.WinTun/Internal/AsyncEventHandle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Magma.WinTun.Internal 8 | { 9 | internal class AsyncEventHandle 10 | { 11 | private volatile TaskCompletionSource m_tcs = new TaskCompletionSource(); 12 | 13 | public Task WaitAsync() => m_tcs.Task; 14 | 15 | public void Set() => m_tcs.TrySetResult(true); 16 | 17 | public void Reset() 18 | { 19 | while (true) 20 | { 21 | var tcs = m_tcs; 22 | if (!tcs.Task.IsCompleted || 23 | Interlocked.CompareExchange(ref m_tcs, new TaskCompletionSource(), tcs) == tcs) 24 | return; 25 | } 26 | } 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Magma.WinTun/Internal/WinTunMemoryPool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using System.Collections.Generic; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace Magma.WinTun.Internal 10 | { 11 | public class WinTunMemoryPool 12 | { 13 | private Queue _pool = new Queue(); 14 | private AsyncEventHandle _emptyEvent = new AsyncEventHandle(); 15 | 16 | public unsafe WinTunMemoryPool(int poolSize, int bufferSize) 17 | { 18 | var totalMemory = poolSize * bufferSize; 19 | var memory = Marshal.AllocHGlobal(totalMemory); 20 | for(var i = 0; i < totalMemory;i+=bufferSize) 21 | { 22 | var mem = new WinTunOwnedMemory(this, memory, i, bufferSize); 23 | _pool.Enqueue(mem); 24 | } 25 | } 26 | 27 | public bool TryGetMemory(out WinTunOwnedMemory memory) 28 | { 29 | lock(_pool) 30 | { 31 | var result = _pool.TryDequeue(out memory); 32 | if (!result) _emptyEvent.Reset(); 33 | return result; 34 | } 35 | } 36 | 37 | private void Return(WinTunOwnedMemory ownedMemory) 38 | { 39 | lock(_pool) 40 | { 41 | _pool.Enqueue(ownedMemory); 42 | _emptyEvent.Set(); 43 | } 44 | } 45 | 46 | public async Task GetMemoryAsync() 47 | { 48 | while(true) 49 | { 50 | lock(_pool) 51 | { 52 | var result = TryGetMemory(out var returnMemory); 53 | if (result) return returnMemory; 54 | } 55 | await _emptyEvent.WaitAsync(); 56 | } 57 | } 58 | 59 | public unsafe class WinTunOwnedMemory : MemoryManager 60 | { 61 | private byte* _memoryPtr; 62 | private int _length; 63 | private WinTunMemoryPool _pool; 64 | 65 | public WinTunOwnedMemory(WinTunMemoryPool memoryPool, IntPtr memoryPtr, int startIndex, int length) 66 | { 67 | _pool = memoryPool; 68 | _memoryPtr = (byte*)memoryPtr.ToPointer() + startIndex; 69 | _length = length; 70 | } 71 | 72 | public unsafe override Span GetSpan() => new Span(_memoryPtr, _length); 73 | 74 | public override MemoryHandle Pin(int elementIndex = 0) => new MemoryHandle(_memoryPtr + elementIndex); 75 | 76 | 77 | public override Memory Memory => CreateMemory(_length); 78 | 79 | public override void Unpin() { } 80 | 81 | protected override void Dispose(bool disposing) => _pool.Return(this); 82 | 83 | public void Return() => _pool.Return(this); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Magma.WinTun/Internal/WinTunPort.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Magma.Network.Abstractions; 9 | using Magma.Network.Header; 10 | using Microsoft.Win32; 11 | using Microsoft.Win32.SafeHandles; 12 | using static Magma.WinTun.Interop.WinIO; 13 | 14 | namespace Magma.WinTun.Internal 15 | { 16 | public class WinTunPort : IDisposable where TPacketReceiver : IPacketReceiver 17 | { 18 | private const string UsermodeDeviceSpace = "\\\\.\\Global\\"; 19 | private const string AdapterKey = "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"; 20 | private const string ConnectionKey = "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"; 21 | 22 | private readonly string _deviceGuid; 23 | private readonly SafeFileHandle _fileHandle; 24 | private readonly FileStream _fileStream; 25 | private readonly WinTunMemoryPool _pool; 26 | private readonly TPacketReceiver _packetReceiver; 27 | private readonly WinTunTransitter _transmitter; 28 | 29 | public WinTunPort(string adapterName, Func packetReceiverFactory) 30 | { 31 | _pool = new WinTunMemoryPool(1000, 2000); 32 | _deviceGuid = GetGuidForName(adapterName); 33 | _fileHandle = CreateFile($"{UsermodeDeviceSpace}{_deviceGuid}.tap", FileAccess.ReadWrite, FileShare.ReadWrite, 0, FileMode.Open, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, IntPtr.Zero); 34 | SetMediaStatus(_fileHandle); 35 | SetTapIOCtl(_fileHandle); 36 | _fileStream = new FileStream(_fileHandle, FileAccess.ReadWrite, 2000, isAsync: true); 37 | _transmitter = new WinTunTransitter(_fileStream, _pool); 38 | _packetReceiver = packetReceiverFactory(_transmitter); 39 | var ignore = ReadLoop(); 40 | } 41 | 42 | private async Task ReadLoop() 43 | { 44 | while (true) 45 | { 46 | var ownedMemory = await _pool.GetMemoryAsync(); 47 | var result = await _fileStream.ReadAsync(ownedMemory.Memory); 48 | TestIpV4(ownedMemory.Memory.Slice(0, result)); 49 | 50 | if (_packetReceiver.TryConsume(ownedMemory) == default) ownedMemory.Return(); 51 | } 52 | } 53 | 54 | private void TestIpV4(Memory input) 55 | { 56 | var eth = Ethernet.TryConsume(input.Span, out var ethernet, out var data); 57 | var ipv4 = IPv4.TryConsume(input.Span, out var ip, out data); 58 | } 59 | 60 | private string GetGuidForName(string adapaterName) 61 | { 62 | foreach (var device in GetDeviceGuids()) 63 | { 64 | var name = GetAdapterName(device); 65 | if (StringComparer.OrdinalIgnoreCase.Compare(name, adapaterName) == 0) 66 | { 67 | return device; 68 | } 69 | } 70 | throw new InvalidOperationException("Unknown name!"); 71 | } 72 | 73 | private string GetAdapterName(string adapterGuid) 74 | { 75 | if (string.IsNullOrWhiteSpace(adapterGuid)) return null; 76 | using (var regKey = Registry.LocalMachine.OpenSubKey($"{ConnectionKey}\\{adapterGuid}\\Connection", writable: false)) 77 | { 78 | var name = regKey.GetValue("Name"); 79 | return name?.ToString(); 80 | } 81 | } 82 | 83 | private IEnumerable GetDeviceGuids() 84 | { 85 | using (var adapters = Registry.LocalMachine.OpenSubKey(AdapterKey, writable: false)) 86 | { 87 | foreach (var adapterName in adapters.GetSubKeyNames()) 88 | { 89 | if (!int.TryParse(adapterName, out _)) continue; 90 | using (var adapter = adapters.OpenSubKey(adapterName, writable: false)) 91 | { 92 | yield return adapter.GetValue("NetCfgInstanceId").ToString(); 93 | } 94 | } 95 | } 96 | } 97 | 98 | public void Dispose() => _fileHandle.Dispose(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/Magma.WinTun/Internal/WinTunTransitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Magma.Network.Abstractions; 8 | 9 | namespace Magma.WinTun.Internal 10 | { 11 | public class WinTunTransitter : IPacketTransmitter 12 | { 13 | private readonly FileStream _fileStream; 14 | private readonly WinTunMemoryPool _pool; 15 | 16 | internal WinTunTransitter(FileStream fileStream, WinTunMemoryPool pool) 17 | { 18 | _fileStream = fileStream; 19 | _pool = pool; 20 | } 21 | 22 | public bool TryGetNextBuffer(out Memory buffer) 23 | { 24 | if (_pool.TryGetMemory(out var memManager)) 25 | { 26 | buffer = memManager.Memory; 27 | return true; 28 | } 29 | buffer = default; 30 | return false; 31 | } 32 | 33 | public uint RandomSequenceNumber() => (uint)(new Random().Next()); 34 | 35 | public async Task SendBuffer(ReadOnlyMemory buffer) 36 | { 37 | if (!MemoryMarshal.TryGetMemoryManager(buffer, out WinTunMemoryPool.WinTunOwnedMemory manager)) throw new InvalidOperationException(); 38 | await _fileStream.WriteAsync(buffer); 39 | manager.Return(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Magma.WinTun/Interop/WinIO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using Microsoft.Win32.SafeHandles; 7 | 8 | namespace Magma.WinTun.Interop 9 | { 10 | internal class WinIO 11 | { 12 | public const int FILE_ATTRIBUTE_SYSTEM = 0x4; 13 | public const int FILE_FLAG_OVERLAPPED = 0x40000000; 14 | private const uint FILE_DEVICE_UNKNOWN = 0x00000022; 15 | private const uint FILE_ANY_ACCESS = 0; 16 | private const uint METHOD_BUFFERED = 0; 17 | 18 | [DllImport("kernel32")] 19 | internal static extern SafeFileHandle CreateFile(string filename, FileAccess fileAccess, FileShare fileShare, int securityAttribs, FileMode fileMode, int flags, IntPtr extraData); 20 | 21 | [DllImport("kernel32")] 22 | private unsafe static extern bool DeviceIoControl(SafeFileHandle deviceHandle, uint dwIoControlCode, void* lpInBuffer, uint nInBufferSize, void* lpOutBuffer, uint nOutBufferSize, out int lpBytesReturned, IntPtr lpOverlapped); 23 | 24 | internal unsafe static void SetMediaStatus(SafeFileHandle deviceHandle) 25 | { 26 | var pStatus = 1; 27 | var result = DeviceIoControl(deviceHandle, TAP_CONTROL_CODE(6, METHOD_BUFFERED), &pStatus, sizeof(int), &pStatus, 4, out var len, IntPtr.Zero); 28 | if (!result) throw new InvalidOperationException("Failed to set media status"); 29 | } 30 | 31 | internal unsafe static void SetTapIOCtl(SafeFileHandle deviceHandle) 32 | { 33 | var inputValues = stackalloc int[3]; 34 | inputValues[0] = 0x0100030A; 35 | inputValues[1] = 0x0000030A; 36 | inputValues[2] = unchecked((int)0x00FFFFFF); 37 | var result = DeviceIoControl(deviceHandle, TAP_CONTROL_CODE(10, METHOD_BUFFERED), inputValues, 12, inputValues, 12, out var len, IntPtr.Zero); 38 | if (!result) throw new InvalidOperationException("Failed to set tun setup"); 39 | } 40 | 41 | private static uint CTL_CODE(uint DeviceType, uint Function, uint Method, uint Access) 42 | { 43 | return ((DeviceType << 16) | (Access << 14) | (Function << 2) | Method); 44 | } 45 | 46 | static uint TAP_CONTROL_CODE(uint request, uint method) 47 | { 48 | return CTL_CODE(FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Magma.WinTun/Magma.WinTun.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | true 6 | latest 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Magma.WinTun/WinTunTransportReceiver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using Magma.Network.Abstractions; 6 | 7 | namespace Magma.WinTun 8 | { 9 | public class WinTunTransportReceiver : IPacketReceiver 10 | { 11 | public void FlushPendingAcks() 12 | { 13 | throw new NotImplementedException(); 14 | } 15 | 16 | public T TryConsume(T input) where T : IMemoryOwner 17 | { 18 | throw new NotImplementedException(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/Magma.Common.Facts/ChecksumFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Magma.Network; 6 | using Magma.Network.Header; 7 | using Xunit; 8 | 9 | namespace Magma.Common.Facts 10 | { 11 | public class ChecksumFacts 12 | { 13 | [Theory] 14 | [InlineData(new byte[] {0x08, 0x00, 0x96, 0x5b, 0x00, 0x01, 0x00, 0xA3, 0x61})] 15 | [InlineData(new byte[] {0x08, 0x00, 0x95, 0xF8, 0x00, 0x01, 0x00, 0xA4, 0x61, 0x62})] 16 | [InlineData(new byte[] {0x08, 0x00, 0x95, 0xE2, 0x00, 0x01, 0x00, 0xBa, 0x61, 0x62})] 17 | [InlineData(new byte[] {0x08, 0x00, 0x32, 0xE0, 0x00, 0x01, 0x00, 0xBC, 0x61, 0x62, 0x63})] 18 | [InlineData(new byte[] {0x08, 0x00, 0x32, 0x7B, 0x00, 0x01, 0x00, 0xBD, 0x61, 0x62, 0x63, 0x64})] 19 | public unsafe void IcpmChecksum(byte[] IcpmPacket) 20 | { 21 | var input = new Span(IcpmPacket); 22 | 23 | Assert.True(IcmpV4.TryConsume(input, out var icmpIn, out var data)); 24 | 25 | var checksum = icmpIn.HeaderChecksum; 26 | 27 | Assert.Equal(0, Checksum.Calculate(ref MemoryMarshal.GetReference(input), IcpmPacket.Length)); 28 | 29 | icmpIn.HeaderChecksum = 0; 30 | var changedData = new Span(&icmpIn, Unsafe.SizeOf()); 31 | changedData.CopyTo(input); 32 | 33 | var newChecksum = Checksum.Calculate(ref MemoryMarshal.GetReference(input), IcpmPacket.Length); 34 | 35 | Assert.Equal(checksum, newChecksum); 36 | } 37 | 38 | [Theory] 39 | [InlineData(new byte[] { 0x08, 0x00, 0x32, 0x7B, 0x00, 0x01, 0x00, 0xBD, 0x61, 0x62, 0x63, 0x64 })] 40 | public unsafe void SplitChecksum(byte[] IcpmPacket) 41 | { 42 | var input = new Span(IcpmPacket); 43 | 44 | Assert.True(IcmpV4.TryConsume(input, out var icmpIn, out var data)); 45 | 46 | var checksum = icmpIn.HeaderChecksum; 47 | 48 | Assert.Equal(0, Checksum.Calculate(ref MemoryMarshal.GetReference(input), IcpmPacket.Length)); 49 | 50 | icmpIn.HeaderChecksum = 0; 51 | var changedData = new Span(&icmpIn, Unsafe.SizeOf()); 52 | changedData.CopyTo(input); 53 | 54 | var input1 = input.Slice(0, 6); 55 | var input2 = input.Slice(6); 56 | 57 | var partial = Checksum.PartialCalculate(ref MemoryMarshal.GetReference(input), input1.Length); 58 | var newChecksum = Checksum.Calculate(ref MemoryMarshal.GetReference(input2), input2.Length, partial); 59 | 60 | Assert.Equal(checksum, newChecksum); 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /test/Magma.Common.Facts/Magma.Common.Facts.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 9 | 10 | true 11 | 12 | 13 | 14 | true 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /test/Magma.Internet.Ip.Facts/HexUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Magma.Internet.Ip.Facts 7 | { 8 | public static class HexUtils 9 | { 10 | public static byte[] HexToByteArray(this string hex) 11 | { 12 | hex = string.Join("", hex.Where(c => !char.IsWhiteSpace(c) && c != '-')); 13 | var NumberChars = hex.Length; 14 | var bytes = new byte[NumberChars / 2]; 15 | for (var i = 0; i < NumberChars; i += 2) 16 | { 17 | bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); 18 | } 19 | return bytes; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/Magma.Internet.Ip.Facts/IPFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using Magma.Network.Header; 6 | using Xunit; 7 | using static Magma.Network.IPAddress; 8 | 9 | namespace Magma.Internet.Ip.Facts 10 | { 11 | public class IPFacts 12 | { 13 | private static readonly string _ipHeader = "45 00 00 34 1f a2 40 00 80 06 bf b4 ac 12 e1 a1 ac 12 e1 a6"; 14 | private static readonly V4Address _sourceAddress = new V4Address(172, 18, 225, 161); 15 | private static readonly V4Address _destAddress = new V4Address(172, 18, 225, 166); 16 | 17 | [Fact] 18 | public void CanReadIPHeader() 19 | { 20 | var array = _ipHeader.HexToByteArray().AsSpan(); 21 | 22 | var ipHeader = Unsafe.As(ref MemoryMarshal.GetReference(array)); 23 | 24 | Assert.Equal(4, ipHeader.Version); 25 | Assert.Equal(5, ipHeader.InternetHeaderLength); 26 | Assert.Equal(20, ipHeader.HeaderLength); 27 | Assert.Equal(_sourceAddress, ipHeader.SourceAddress); 28 | Assert.Equal(_destAddress, ipHeader.DestinationAddress); 29 | Assert.True(ipHeader.DontFragment); 30 | Assert.Equal(ProtocolNumber.Tcp, ipHeader.Protocol); 31 | Assert.Equal(32, ipHeader.DataLength); 32 | Assert.Equal(20, ipHeader.HeaderLength); 33 | Assert.Equal(52, ipHeader.TotalLength); 34 | Assert.Equal(41503, ipHeader.Identification); 35 | } 36 | 37 | [Fact] 38 | public void WriteIPHeader() 39 | { 40 | var span = (Enumerable.Repeat(0xFF, 20).ToArray()).AsSpan(); 41 | //Fill it with junk 42 | 43 | ref var ipHeader = ref Unsafe.As(ref MemoryMarshal.GetReference(span)); 44 | IPv4.InitHeader(ref ipHeader, _sourceAddress, _destAddress, 32, ProtocolNumber.Tcp, 41503); 45 | 46 | Assert.True(_ipHeader.HexToByteArray().AsSpan().SequenceEqual(span)); 47 | } 48 | 49 | [Fact] 50 | public void InternetHeaderLength() 51 | { 52 | var ipHeader = new IPv4() 53 | { 54 | InternetHeaderLength = 5, 55 | }; 56 | 57 | Assert.Equal(5, ipHeader.InternetHeaderLength); 58 | Assert.Equal(20, ipHeader.HeaderLength); 59 | } 60 | 61 | [Fact] 62 | public void HeaderLength() 63 | { 64 | var ipHeader = new IPv4() 65 | { 66 | HeaderLength = 20, 67 | }; 68 | 69 | Assert.Equal(5, ipHeader.InternetHeaderLength); 70 | Assert.Equal(20, ipHeader.HeaderLength); 71 | } 72 | 73 | [Fact] 74 | public void HeaderAndVersionWorkTogether() 75 | { 76 | var ipHeader = new IPv4() 77 | { 78 | InternetHeaderLength = 5, 79 | Version = 4, 80 | }; 81 | 82 | Assert.Equal(4, ipHeader.Version); 83 | Assert.Equal(5, ipHeader.InternetHeaderLength); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /test/Magma.Internet.Ip.Facts/Magma.Internet.Ip.Facts.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/Magma.Internet.Ip.Facts/TcpConnectionFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using Magma.Network.Header; 6 | using Magma.Transport.Tcp; 7 | using Magma.Transport.Tcp.Header; 8 | using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; 9 | using Xunit; 10 | 11 | namespace Magma.Internet.Ip.Facts 12 | { 13 | public class TcpConnectionFacts 14 | { 15 | //private static readonly byte[] s_synPacket = "74 ea 3a 38 f1 00 00 19 d2 90 29 61 08 00 45 00 00 3c 20 e3 40 00 40 06 d9 2d c0 a8 01 68 d8 12 a6 88 c2 c3 00 50 d5 e2 df b4 00 00 00 00 a0 02 20 00 c4 47 00 00 02 04 05 b4 01 03 03 02 04 02 08 0a 00 04 aa 62 00 00 00 00".HexToByteArray(); 16 | //private static readonly byte[] s_synAckPacket = "00 19 d2 90 29 61 74 ea 3a 38 f1 00 08 00 45 00 00 3c 00 00 40 00 2d 06 0d 11 d8 12 a6 88 c0 a8 01 68 00 50 c2 c3 29 91 a6 b8 d5 e2 df b5 a0 12 16 a0 f8 e7 00 00 02 04 05 a0 01 01 08 0a 4e 62 b9 10 00 04 aa 62 01 03 03 09".HexToByteArray(); 17 | //private static readonly byte[] s_ackPacket = "74 ea 3a 38 f1 00 00 19 d2 90 29 61 08 00 45 00 00 34 20 e4 40 00 40 06 d9 34 c0 a8 01 68 d8 12 a6 88 c2 c3 00 50 d5 e2 df b5 29 91 a6 b9 80 10 10 bc 2a 66 00 00 01 01 08 0a 00 04 aa 81 4e 62 b9 10".HexToByteArray(); 18 | 19 | //[Fact] 20 | //public void ThreeWayHandshakeWorks() 21 | //{ 22 | // var synSpan = s_synPacket.AsSpan(); 23 | // var synAckSpan = s_synAckPacket.AsSpan(); 24 | 25 | // Assert.True(Ethernet.TryConsume(synAckSpan, out var synAckEthHeader, out var data)); 26 | // Assert.True(IPv4.TryConsume(data, out var synAckIpHeader, out data)); 27 | // Assert.True(TcpHeaderWithOptions.TryConsume(data, out var synAckTcpHeader, out data)); 28 | 29 | // Assert.True(Ethernet.TryConsume(synSpan, out var etherHeader, out data)); 30 | // Assert.True(IPv4.TryConsume(data, out var ipHeader, out data, true)); 31 | // Assert.True(TcpHeaderWithOptions.TryConsume(data, out var tcpHeader, out data)); 32 | 33 | // var connection = new TestTcpConnection(etherHeader, ipHeader, tcpHeader.Header, null); 34 | // connection.ProcessPacket(tcpHeader, data); 35 | //} 36 | 37 | 38 | //private class TestTcpConnection : TcpConnection 39 | //{ 40 | // public TestTcpConnection(Ethernet etherHeader, IPv4 ipHeader, Tcp tcpHeader, IConnectionDispatcher connectionDisptacher) 41 | // : base(etherHeader, ipHeader, tcpHeader, System.IO.Pipelines.PipeScheduler.ThreadPool, System.IO.Pipelines.PipeScheduler.ThreadPool, MemoryPool.Shared, connectionDisptacher) 42 | // { 43 | // } 44 | 45 | // protected override uint GetRandomSequenceStart() => 697411256; 46 | 47 | // protected override uint GetTimestamp() => 1315092752; 48 | 49 | // protected override bool TryGetMemory(out Memory memory) 50 | // { 51 | // memory = (new byte[2048]).AsMemory(); 52 | // return true; 53 | // } 54 | 55 | // protected override void WriteMemory(Memory memory) 56 | // { 57 | // for(var i = 0; i < memory.Length;i++) 58 | // { 59 | // Assert.Equal(s_synAckPacket[i], memory.Span[i]); 60 | // } 61 | // } 62 | //} 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /test/Magma.Internet.Ip.Facts/TcpFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.CompilerServices; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using Magma.Network; 8 | using Magma.Network.Header; 9 | using Magma.Transport.Tcp.Header; 10 | using Xunit; 11 | using static Magma.Network.IPAddress; 12 | 13 | namespace Magma.Internet.Ip.Facts 14 | { 15 | public class TcpFacts 16 | { 17 | private static readonly ushort _sourcePort = 49859; 18 | private static readonly ushort _destPort = 80; 19 | private static readonly byte[] _tcpSynPacketWithOptions = "c2 c3 00 50 d5 e2 df b4 00 00 00 00 a0 02 20 00 c4 47 00 00 02 04 05 b4 01 03 03 02 04 02 08 0a 00 04 aa 62 00 00 00 00".HexToByteArray(); 20 | private static readonly byte[] _tcpSynPacket = _tcpSynPacketWithOptions.AsSpan().Slice(0, 20).ToArray(); 21 | private static readonly uint _synSequenceNumber = 3588415412; 22 | private static readonly V4Address _sourceAddress = new V4Address(192, 168, 1, 104); 23 | private static readonly V4Address _destAddress = new V4Address(216, 18, 166, 136); 24 | 25 | private static readonly byte[] _tcpActualDataPacket = "ED-79-1B-58-40-7F-8B-F0-21-45-3E-A7-50-18-04-05-D8-8C-00-00-47-45-54-20-2F-20-48-54-54-50-2F-31-2E-31-0D-0A-55-73-65-72-2D-41-67-65-6E-74-3A-20-57-67-65-74-2F-31-2E-31-37-2E-31-20-28-6C-69-6E-75-78-2D-67-6E-75-29-0D-0A-41-63-63-65-70-74-3A-20-2A-2F-2A-0D-0A-41-63-63-65-70-74-2D-45-6E-63-6F-64-69-6E-67-3A-20-69-64-65-6E-74-69-74-79-0D-0A-48-6F-73-74-3A-20-31-37-32-2E-31-38-2E-32-32-35-2E-31-36-36-3A-37-30-30-30-0D-0A-43-6F-6E-6E-65-63-74-69-6F-6E-3A-20-4B-65-65-70-2D-41-6C-69-76-65-0D-0A-0D-0A".HexToByteArray(); 26 | 27 | [Fact] 28 | public void BadPacket() 29 | { 30 | Assert.True(TcpHeaderWithOptions.TryConsume(_tcpActualDataPacket.AsSpan(), out var header, out var data)); 31 | } 32 | 33 | [Fact] 34 | public void CanReadTcpSyn() 35 | { 36 | var span = _tcpSynPacketWithOptions.AsSpan(); 37 | 38 | var tcpHeader = Unsafe.As(ref MemoryMarshal.GetReference(span)); 39 | 40 | Assert.Equal(_sourcePort, tcpHeader.SourcePort); 41 | Assert.Equal(_destPort, tcpHeader.DestinationPort); 42 | Assert.Equal(0, tcpHeader.UrgentPointer); 43 | Assert.True(tcpHeader.SYN); 44 | Assert.False(tcpHeader.ACK); 45 | Assert.False(tcpHeader.CWR); 46 | Assert.False(tcpHeader.NS); 47 | Assert.False(tcpHeader.RST); 48 | Assert.False(tcpHeader.PSH); 49 | Assert.False(tcpHeader.URG); 50 | Assert.Equal(8192, tcpHeader.WindowSize); 51 | Assert.Equal(_synSequenceNumber, tcpHeader.SequenceNumber); 52 | Assert.Equal(0u, tcpHeader.AcknowledgmentNumber); 53 | Assert.Equal(10, tcpHeader.DataOffset); 54 | } 55 | 56 | [Fact] 57 | public void TryConsumeSynPacket() 58 | { 59 | var span = _tcpSynPacketWithOptions.AsSpan(); 60 | 61 | Assert.True(Tcp.TryConsume(span, out var tcp, out var options, out var data)); 62 | Assert.Equal(20, options.Length); 63 | Assert.Equal(0, data.Length); 64 | } 65 | 66 | [Fact] 67 | public void TryConsumeSynPacketWithOptions() 68 | { 69 | var span = _tcpSynPacketWithOptions.AsSpan(); 70 | 71 | Assert.True(TcpHeaderWithOptions.TryConsume(span, out var header, out var data)); 72 | Assert.True(header.SackPermitted); 73 | Assert.Equal(1460, header.MaximumSegmentSize); 74 | Assert.Equal(2, header.WindowScale); 75 | } 76 | 77 | [Fact] 78 | public void CanWriteTcpSyn() 79 | { 80 | var span = Enumerable.Repeat(0xFF, 10 * 4).ToArray().AsSpan(); 81 | _tcpSynPacketWithOptions.AsSpan().Slice(20).CopyTo(span.Slice(20)); 82 | ref var tcpHeader = ref Unsafe.As(ref MemoryMarshal.GetReference(span)); 83 | tcpHeader.AcknowledgmentNumber = 0; 84 | tcpHeader.Checksum = 0; 85 | tcpHeader.DestinationPort = _destPort; 86 | tcpHeader.SourcePort = _sourcePort; 87 | 88 | tcpHeader.UrgentPointer = 0; 89 | tcpHeader.SequenceNumber = _synSequenceNumber; 90 | tcpHeader.NS = false; 91 | tcpHeader.CWR = false; 92 | tcpHeader.ECE = false; 93 | tcpHeader.URG = false; 94 | tcpHeader.ACK = false; 95 | tcpHeader.PSH = false; 96 | tcpHeader.RST = false; 97 | tcpHeader.SYN = true; 98 | tcpHeader.FIN = false; 99 | tcpHeader.DataOffset = 10; 100 | tcpHeader.WindowSize = 8192; 101 | 102 | var pseudoHeader = new TcpV4PseudoHeader() 103 | { 104 | Destination = _destAddress, 105 | Source = _sourceAddress, 106 | ProtocolNumber = ProtocolNumber.Tcp, 107 | Reserved = 0, 108 | }; 109 | 110 | var temp = Checksum.PartialCalculate(ref Unsafe.As(ref pseudoHeader), Unsafe.SizeOf()); 111 | tcpHeader.SetChecksum(span, temp); 112 | 113 | Assert.Equal(_tcpSynPacket, span.Slice(0, 20).ToArray()); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /test/Magma.Internet.Ip.Facts/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "methodDisplay": "method", 3 | "longRunningTestSeconds": 5 4 | } 5 | -------------------------------------------------------------------------------- /test/Magma.Link.Facts/HexUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Magma.Link.Facts 7 | { 8 | public static class HexUtils 9 | { 10 | public static byte[] HexToByteArray(this string hex) 11 | { 12 | hex = string.Join("", hex.Where(c => !char.IsWhiteSpace(c) && c != '-')); 13 | var NumberChars = hex.Length; 14 | var bytes = new byte[NumberChars / 2]; 15 | for (var i = 0; i < NumberChars; i += 2) 16 | { 17 | bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); 18 | } 19 | return bytes; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/Magma.Link.Facts/Magma.Link.Facts.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /test/Magma.Link.Facts/ParseEthernetHeader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | 4 | namespace Magma.Link.Facts 5 | { 6 | public class ParseEthernetHeader 7 | { 8 | private const string ValidFrame = "00-15-5D-F3-43-11-82-15-F5-19-E6-94-08-00-45-00-00-4C-15-DF-00-00-80-06-A0-69-AC-1E-96-11-AC-1E-96-15-F8-06-00-16-7D-73-6D-5B-C9-25-AE-7E-50-18-20-14-27-43-00-00-C8-48-2D-EE-E4-89-04-C5-56-4B-04-51-C5-C7-08-8D-47-06-E6-1E-EF-E1-D8-BE-FB-AF-BD-83-BB-36-A0-3B-3F-73-37-07"; 9 | 10 | [Fact] 11 | public void EthernetLengthCorrect() 12 | { 13 | var frame = ValidFrame.HexToByteArray().AsSpan(); 14 | 15 | Assert.True(Network.Header.Ethernet.TryConsume(frame, out var ethernet, out var data)); 16 | 17 | Assert.Equal(76, data.Length); 18 | } 19 | 20 | [Fact] 21 | public void FromMacAddressCorrect() 22 | { 23 | var frame = ValidFrame.HexToByteArray().AsSpan(); 24 | Assert.True(Network.Header.Ethernet.TryConsume(frame, out var ethernet, out var data)); 25 | 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/Magma.Link.Facts/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "methodDisplay": "method", 3 | "longRunningTestSeconds": 5 4 | } 5 | -------------------------------------------------------------------------------- /test/Magma.NetMap.Facts/Magma.NetMap.Facts.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | true 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/Magma.NetMap.Facts/NetMapInteropFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Magma.NetMap.Interop; 5 | using Xunit; 6 | 7 | namespace Magma.NetMap.Facts 8 | { 9 | public class NetMapInteropFacts 10 | { 11 | [Fact(Skip = "Can only run on a machine with netmap")] 12 | public unsafe void CanOpenDevice() 13 | { 14 | //NetMap.Interop.NetMapInterop.nm_open("eth0", default(nmreq), 0, null); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/Magma.NetMap.Facts/StructFacts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using Magma.NetMap.Interop; 4 | using Xunit; 5 | using static Magma.NetMap.Interop.Netmap; 6 | 7 | namespace Magma.NetMap.Facts 8 | { 9 | public class StructFacts 10 | { 11 | [Fact] 12 | public void CheckRequestSizeMatchesNative() 13 | { 14 | var expectedSize = 60; 15 | var actual = Unsafe.SizeOf(); 16 | Assert.Equal(expectedSize, actual); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/Magma.NetMap.Facts/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "methodDisplay": "method", 3 | "longRunningTestSeconds": 5 4 | } 5 | -------------------------------------------------------------------------------- /version.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 0.0.1 4 | beta 5 | 6 | 7 | --------------------------------------------------------------------------------