├── .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 | 
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 |
--------------------------------------------------------------------------------