Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate SoftNPU as virtual hardware #2089

Merged
merged 9 commits into from
Mar 22, 2023
Merged

Conversation

internet-diglett
Copy link
Contributor

@internet-diglett internet-diglett commented Dec 21, 2022

  • Automate creation of softnpu zone
  • Automate startup up softnpu
  • Automate configuration of softnpu
    • Configure link addresses and gateway parameters via scadm / softnpuadm upon softnpu startup
    • Enable communication between dendrite and softnpu
      • Have switch zone mount softnpu socket
      • package dendrite with softnpu feature
      • When dendrite asic type is Softnpu, have dendrite configure switch via softnpu socketfile
    • Configure arp and nat entries via dendrite upon guest VM creation
      • Add logic to dendrite-softnpu for programming nat entries
    • cleanup nat entries upon guest VM deletion

Closes #1465
Closes oxidecomputer/opte#236

@rcgoodfellow rcgoodfellow changed the title Integrate softnpu Integrate SoftNPP as virtual hardware Jan 31, 2023
@internet-diglett internet-diglett changed the title Integrate SoftNPP as virtual hardware Integrate SoftNPU as virtual hardware Jan 31, 2023
@morlandi7 morlandi7 added the networking Related to the networking. label Feb 7, 2023
@rcgoodfellow
Copy link
Contributor

Just dropping a note that I was able to successfully test this end-to-end with the CI builds brought in via the setup-virtual-hardware scripts. From zero to instances talking to the Internet.

@rcgoodfellow
Copy link
Contributor

Looks like destroy virtual hardware needs a bit of love.

ry@masaka:~/src/omicron$ pfexec ./tools/destroy_virtual_hardware.sh
+++ dirname ./tools/destroy_virtual_hardware.sh
++ cd ./tools
++ pwd
+ SOURCE_DIR=/home/ry/src/omicron/tools
+ cd /home/ry/src/omicron/tools/..
+ OMICRON_TOP=/home/ry/src/omicron
+ MARKER=/etc/opt/oxide/NO_INSTALL
+ [[ -f /etc/opt/oxide/NO_INSTALL ]]
++ id -u
+ [[ 0 -ne 0 ]]
+ verify_omicron_uninstalled
+ svcs svc:/system/illumos/sled-agent:default
svcs: Pattern 'svc:/system/illumos/sled-agent:default' doesn't match any instances
+ unload_xde_driver
++ modinfo
++ grep xde
++ cut -d ' ' -f 1
+ local ID=239
+ [[ -n 239 ]]
+ modunload -i 239
+ success 'Verified the xde kernel driver is unloaded'
+ set +x
Verified the xde kernel driver is unloaded
+ try_remove_vnics
+ try_remove_address lo0/underlay
+ local ADDRESS=lo0/underlay
++ ipadm show-addr -p -o addr lo0/underlay
ipadm: Address object not found+ [[ -n '' ]]
+ success 'Verified address lo0/underlay does not exist'
+ set +x
Verified address lo0/underlay does not exist
+ try_remove_interface sc0_1
+ local IFACE=sc0_1
++ ipadm show-if -p -o IFNAME sc0_1
ipadm: Could not get interface(s): Interface does not exist+ [[ -n '' ]]
+ success 'Verified IP interface sc0_1 does not exist'
+ set +x
Verified IP interface sc0_1 does not exist
+ try_remove_vnic sc0_1
+ local LINK=sc0_1
++ dladm show-vnic -p -o LINK sc0_1
+ [[ -n sc0_1 ]]
+ dladm delete-vnic sc0_1
dladm: vnic deletion failed: link busy
+ warn 'Failed to delete VNIC link sc0_1'
+ set +x
Failed to delete VNIC link sc0_1
+ success 'Verified VNIC link sc0_1 does not exist'
+ set +x
Verified VNIC link sc0_1 does not exist
+ INDICES=("0" "1")
+ for I in "${INDICES[@]}"
+ try_remove_simnet net0
+ local LINK=net0
++ dladm show-simnet -p -o LINK net0
+ [[ -n net0 ]]
+ dladm delete-simnet -t net0
dladm: simnet deletion failed: link busy
+ warn 'Failed to delete simnet link net0'
+ set +x
Failed to delete simnet link net0
+ success 'Verified simnet link net0 does not exist'
+ set +x
Verified simnet link net0 does not exist
+ try_remove_simnet sc0_0
+ local LINK=sc0_0
++ dladm show-simnet -p -o LINK sc0_0
+ [[ -n sc0_0 ]]
+ dladm delete-simnet -t sc0_0
dladm: simnet deletion failed: link busy
+ warn 'Failed to delete simnet link sc0_0'
+ set +x
Failed to delete simnet link sc0_0
+ success 'Verified simnet link sc0_0 does not exist'
+ set +x
Verified simnet link sc0_0 does not exist
+ try_remove_simnet sr0_0
+ local LINK=sr0_0
++ dladm show-simnet -p -o LINK sr0_0
+ [[ -n sr0_0 ]]
+ dladm delete-simnet -t sr0_0
dladm: simnet deletion failed: link busy
+ warn 'Failed to delete simnet link sr0_0'
+ set +x
Failed to delete simnet link sr0_0
+ success 'Verified simnet link sr0_0 does not exist'
+ set +x
Verified simnet link sr0_0 does not exist
+ try_remove_simnet scr0_0
+ local LINK=scr0_0
++ dladm show-simnet -p -o LINK scr0_0
+ [[ -n scr0_0 ]]
+ dladm delete-simnet -t scr0_0
+ success 'Verified simnet link scr0_0 does not exist'
+ set +x
Verified simnet link scr0_0 does not exist
+ for I in "${INDICES[@]}"
+ try_remove_simnet net1
+ local LINK=net1
++ dladm show-simnet -p -o LINK net1
+ [[ -n net1 ]]
+ dladm delete-simnet -t net1
dladm: simnet deletion failed: link busy
+ warn 'Failed to delete simnet link net1'
+ set +x
Failed to delete simnet link net1
+ success 'Verified simnet link net1 does not exist'
+ set +x
Verified simnet link net1 does not exist
+ try_remove_simnet sc1_0
+ local LINK=sc1_0
++ dladm show-simnet -p -o LINK sc1_0
+ [[ -n sc1_0 ]]
+ dladm delete-simnet -t sc1_0
+ success 'Verified simnet link sc1_0 does not exist'
+ set +x
Verified simnet link sc1_0 does not exist
+ try_remove_simnet sr0_1
+ local LINK=sr0_1
++ dladm show-simnet -p -o LINK sr0_1
+ [[ -n sr0_1 ]]
+ dladm delete-simnet -t sr0_1
dladm: simnet deletion failed: link busy
+ warn 'Failed to delete simnet link sr0_1'
+ set +x
Failed to delete simnet link sr0_1
+ success 'Verified simnet link sr0_1 does not exist'
+ set +x
Verified simnet link sr0_1 does not exist
+ try_remove_simnet scr0_1
+ local LINK=scr0_1
++ dladm show-simnet -p -o LINK scr0_1
+ [[ -n scr0_1 ]]
+ dladm delete-simnet -t scr0_1
+ success 'Verified simnet link scr0_1 does not exist'
+ set +x
Verified simnet link scr0_1 does not exist
+ try_destroy_zpools
+ readarray -t ZPOOLS
++ zfs list -d 0 -o name
++ grep '^oxp_'
+ for ZPOOL in "${ZPOOLS[@]}"
+ VDEV_FILE=/home/ry/src/omicron/oxp_d462a7f7-b628-40fe-80ff-4e4189e2d62b.vdev
+ zfs destroy -r oxp_d462a7f7-b628-40fe-80ff-4e4189e2d62b
+ zfs unmount oxp_d462a7f7-b628-40fe-80ff-4e4189e2d62b
+ zpool destroy oxp_d462a7f7-b628-40fe-80ff-4e4189e2d62b
+ rm -f /home/ry/src/omicron/oxp_d462a7f7-b628-40fe-80ff-4e4189e2d62b.vdev
+ success 'Verified ZFS pool and vdev oxp_d462a7f7-b628-40fe-80ff-4e4189e2d62b does not exist'
+ set +x
Verified ZFS pool and vdev oxp_d462a7f7-b628-40fe-80ff-4e4189e2d62b does not exist
+ for ZPOOL in "${ZPOOLS[@]}"
+ VDEV_FILE=/home/ry/src/omicron/oxp_e4b4dc87-ab46-49fb-a4b4-d361ae214c03.vdev
+ zfs destroy -r oxp_e4b4dc87-ab46-49fb-a4b4-d361ae214c03
+ zfs unmount oxp_e4b4dc87-ab46-49fb-a4b4-d361ae214c03
+ zpool destroy oxp_e4b4dc87-ab46-49fb-a4b4-d361ae214c03
+ rm -f /home/ry/src/omicron/oxp_e4b4dc87-ab46-49fb-a4b4-d361ae214c03.vdev
+ success 'Verified ZFS pool and vdev oxp_e4b4dc87-ab46-49fb-a4b4-d361ae214c03 does not exist'
+ set +x
Verified ZFS pool and vdev oxp_e4b4dc87-ab46-49fb-a4b4-d361ae214c03 does not exist
+ for ZPOOL in "${ZPOOLS[@]}"
+ VDEV_FILE=/home/ry/src/omicron/oxp_f4b4dc87-ab46-49fb-a4b4-d361ae214c03.vdev
+ zfs destroy -r oxp_f4b4dc87-ab46-49fb-a4b4-d361ae214c03
+ zfs unmount oxp_f4b4dc87-ab46-49fb-a4b4-d361ae214c03
+ zpool destroy oxp_f4b4dc87-ab46-49fb-a4b4-d361ae214c03
+ rm -f /home/ry/src/omicron/oxp_f4b4dc87-ab46-49fb-a4b4-d361ae214c03.vdev
+ success 'Verified ZFS pool and vdev oxp_f4b4dc87-ab46-49fb-a4b4-d361ae214c03 does not exist'
+ set +x
Verified ZFS pool and vdev oxp_f4b4dc87-ab46-49fb-a4b4-d361ae214c03 does not exist
+ remove_softnpu_zone
+ zoneadm -z softnpu halt
+ zoneadm -z softnpu uninstall -F
INFO: omicron: uninstalling zone softnpu...
+ zonecfg -z softnpu delete -F
+ rm -rf /opt/softnpu/stuff
+ zfs destroy rpool/softnpu-zone
+ rm -rf /softnpu-zone
ry@masaka:~/src/omicron$ ipadm
ADDROBJ           TYPE     STATE        ADDR
lo0/v4            static   ok           127.0.0.1/8
igb0/dhcp         dhcp     ok           192.168.1.4/24
lo0/v6            static   ok           ::1/128
igb0/v6           addrconf ok           fe80::aa5e:45ff:fecd:bd94/10
igb0/v6           addrconf ok           2603:3024:b0b:3400::18ba/128
ry@masaka:~/src/omicron$ ipadm show-if
IFNAME     CLASS     STATE    CURRENT      PERSISTENT
lo0        VIRTUAL   ok       -m-v------46 ---
igb0       IP        ok       bm--------46 -46
net0       IP        down     bm--------46 ---
net1       IP        down     bm--------46 ---
ry@masaka:~/src/omicron$ dladm
LINK        CLASS     MTU    STATE    BRIDGE     OVER
igb0        phys      1500   up       --         --
net0        simnet    1600   up       --         sc0_0
sc0_0       simnet    1600   up       --         net0
sr0_0       simnet    9000   up       --         --
net1        simnet    1600   up       --         --
sr0_1       simnet    9000   up       --         --
sc0_1       vnic      1500   up       --         igb0

@internet-diglett internet-diglett marked this pull request as ready for review February 16, 2023 18:06
@internet-diglett internet-diglett force-pushed the integrate-softnpu branch 2 times, most recently from 4bd4926 to 9fa6e34 Compare February 16, 2023 23:03
@rcgoodfellow
Copy link
Contributor

Looks like destroy virtual hardware needs a bit of love.

This is now working as expected for me.

@rcgoodfellow
Copy link
Contributor

I sorted out the networking issues we were seeing today with softnpu in the following

And pushed an Omicron commit you can cherry-pick into this branch with the updates.

@rcgoodfellow
Copy link
Contributor

Noting that we'll need to update to the current Dendrite API as we are now a few commits behind. This is causing saga failure for me due to API mismatch.

@rcgoodfellow
Copy link
Contributor

rcgoodfellow commented Feb 27, 2023

I've put together a pile of pull requests to fix softnpu/p4 range keys.

These updates collectively solve the issue I was seeing with proxy arp replying to a much larger range than assigned. I think they may also address the NAT range issues @internet-diglett has been observing.

Copy link
Collaborator

@bnaecker bnaecker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not at all a complete review. Just a few notes that came up during chat today, wanted to get them persisted.

let params = sagactx.saga_params::<Params>()?;
let opctx = OpContext::for_saga_action(&sagactx, &params.serialized_authn);
let osagactx = sagactx.user_data();
let dpd_client: Arc<dpd_client::Client> =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to clone, I think, the dpd-client methods we're calling take &self.

nexus/src/app/sagas/instance_create.rs Outdated Show resolved Hide resolved
nexus/src/app/sagas/instance_create.rs Outdated Show resolved Hide resolved
nexus/src/app/sagas/instance_create.rs Outdated Show resolved Hide resolved
.await
}
}
.map_err(|e| {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I think the fact that each request can fail means these need to be separate saga actions. Look at the NIC and disk-creation for examples. We basically create N nodes, for N NICs. Each of those creates (or destroys) exactly one item. Otherwise, the action is not really idempotent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about this more, I'm not certain this is the case. If any of the requests fail, the saga is supposed to unwind, right? Even if the node is re-attempted, any requests that were successful would not be re-sent because of the logic above this that checks for existing nat entries, so unless I'm missing something, I think this is actually idempotent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Persisting our chat discussion here:

I had a misunderstanding of Steno's behavior. Since we don't run undo actions for failed nodes, the logic in sic_remove_network_config() would never be called in the event of a partial failure, which is why the overall behavior would not be idempotent.

}
}

for entry in entries_to_remove {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing here about idempotency. We need a different forward and undo action for each external IP address.

Copy link
Contributor Author

@internet-diglett internet-diglett Mar 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we actually need a separate saga node for each ip, or do we need to make sure that we attempt to delete the nat entry for every existing ip at least once?

Implicitly handled based on above discussion


let mut entries_to_remove: Vec<ExternalIp> = vec![];

for entry in external_ips {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing here about idempotency. We need a different saga action for each external IP I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Persisting convo from chat:

It doesn't seem that any of the other saga nodes in instance_delete.rs are using subsagas, so here we're going to ensure that we have attempted to nat entries for all external ips before checking errors.

rcgoodfellow and others added 6 commits March 20, 2023 21:06
Update softnpu & dendrite dependencies
Update commands in helper scripts to use `swadm`
Update sled-agent config.toml to use softnpu override feature
Update a-to-z docs to reflect new process
@smklein smklein self-requested a review March 21, 2023 17:17
Copy link
Collaborator

@bnaecker bnaecker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tons of great work @internet-diglett! This has been a long slog, your persistence is admirable. I've got a few questions and small comments, but this generally seems pretty solid to me!

.github/buildomat/jobs/build-and-test-linux.sh Outdated Show resolved Hide resolved
common/src/nexus_config.rs Outdated Show resolved Hide resolved
common/src/nexus_config.rs Outdated Show resolved Hide resolved
common/src/nexus_config.rs Outdated Show resolved Hide resolved
sled-agent/src/services.rs Outdated Show resolved Hide resolved
Comment on lines 546 to 550
// TODO: correctness
// I'm not sure whether or not we should be treating softnpu
// as a stub or treating it as a real ASIC here, so this
// might change.
ScrimletMode::Softnpu => HardwareView::new_stub_tofino(true),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This refers to how we look for the driver and devices files. This can't be a "real" Tofino, though we may want to add a variant to TofinoView for the SoftNPU-specific stuff.

sled-hardware/src/lib.rs Outdated Show resolved Hide resolved
smf/sled-agent/non-gimlet/config.toml Outdated Show resolved Hide resolved
tools/setup_path.sh Show resolved Hide resolved
Copy link
Collaborator

@smklein smklein left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Thanks for taking this work on, really appreciate you doing it. Herculean effort, which rockets the effectiveness of the product forward.
  2. I have a smattering of comments about minor changes before we check in, but when in doubt, pretend I added a "do what he said" comment under everything @bnaecker said.
  3. Adding a stub implementation of dpd so cargo test can work without custom environment variables should be a pretty high priority for us to fix -- lmk if I can help get this work done!

Comment on lines 40 to 42
/// TODO: @internet-diglett
/// this is to preserve the old behavior of `scrimlet_override = false`,
/// but I haven't found where that logic has actually been leveraged...
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the old case, this was:

stub_scrimlet = Some(true) -> Please treat the sled as a scrimlet
stub_scrimlet = Some(false) -> Please DO NOT treat the sled as a scrimlet
stub_scrimlet = None -> Use whatever you automatically detect by scanning hardware

Basically, without this option, all "not-on-a-real-rack" deployments of Omicron look like Scrimlets.

Not sure if this case actually matters all too much, but that was the intent, at least.

.cargo/config Outdated
Comment on lines 19 to 21
# We need to enable this to access private repos
[net]
git-fetch-with-cli = true
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. Removing it and seeing what happens in CI.


debug!(log, "checking for existing nat mapping for {target_ip:#?}");

// TODO: currently if we have this environment variable set, we want to
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we file an issue to track this, and label these comments with TODO(issue number)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +51 to +53
----
SKIP_ASIC_CONFIG=1 cargo test
----
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand why this environment variable was added, and I empathize with the tradeoff being made here, but IMO the disposition is hostile to developers.

The property that "you should expect cargo build and cargo test to work" is pretty important - for example, a lot of effort was expended by @davepacheco to ensure that CockroachDB can be automatically started and used by our test suite, without requiring some environment variable setup.

You have comments down below with "TODOs" that we should be using a stub dpd, depending on how the platform was built, and I totally agree with that - we have support in our integration tests for launching an out-of-band CockroachDB for database testing, and we could totally do something similar for the stub dpd - it should be easy to have an in-Omicron implementation that can act as a fake.

That doesn't need to block this PR, fwiw. LMK if you want to pair on getting that stub in place.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed on all points. I would like to work on getting that done ASAP.

/// Configuration for the dataplane daemon.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct DpdConfig {
pub address: SocketAddr,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a real system, where should this address come from? Is it on the underlay network? Would we expect it to have an entry in the internal DNS system?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a real system there will be a dpd instance per scrimlet, each with a unique underlay address. DpdConfig is currently a single-valued member dpd_api in PackageConfig. This works for single-sidecar setups, but we'll need to evolve toward being able to handle multiple dpd instances.

I've noted this in the external networking plumbing I'm currently working on. I'd be happy to take this issue on in that work if we think that makes sense. There is additional infrastructure in the database in that branch that helps to identify the sidecar of interest through rack_id and switch_location when operations come through the API that require dpd interaction.

Comment on lines 92 to 96

Follow the
https://github.com/oxidecomputer/meta/blob/master/engineering/remote-access-preview-demo-setup.adoc#setting-up-the-cli[RAP document]
to set up IPs, images, disks, instances etc. Things to pay particular attention
to here are the following.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an internal document; IMO we should not be referencing it in public-facing docs (not necessarily for secrecy, but just because it won't make sense to anyone working with this codebase who isn't an Oxide employee).

Comment on lines -83 to -92
// TODO-remove
//
// See https://github.com/oxidecomputer/omicron/issues/1337
//
// An additional part of the workaround to connect into instances. This is
// required to tell OPTE to actually act as a 1-1 NAT when an instance is
// provided with an external IP address, rather than do its normal job of
// encapsulating the traffic onto the underlay (such as for delivery to
// boundary services).
use_external_ip_workaround(&log, &xde_conf);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hell yes, love to see this being deleted

illumos-utils/src/opte/illumos/mod.rs Show resolved Hide resolved
@@ -0,0 +1,87 @@
#!/bin/bash
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm kinda opposed to checking this script in - it's very environment-specific, may not make sense for all devs, but has a name which implies "you should run me first!"

I think this sorta thing makes sense for developers to have / use, but it's a bit of a landmine for someone to run without a lot of context about their environment

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make more sense for it to be something like example.sh instead of quickstart.sh? If not, I'm not opposed to removing it altogether.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think an alternative would be to include the script or substantial portions of it in the documentation, such as how-to-run.adoc.

Comment on lines 104 to 107
Once your host has been populated with images, you can use the script at
`tools/quickstart.sh` to quickly create a VM and set up the `proxy-arp`. Please
be sure to edit the `ip_pool_start` and `ip_pool_end` variables to match your
desired address ranges.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment below about this script; IMO we should not check it in.

@bnaecker bnaecker self-requested a review March 22, 2023 02:11
Copy link
Collaborator

@bnaecker bnaecker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing all my questions, @internet-diglett! This all looks OK to me!

@internet-diglett internet-diglett merged commit a796560 into main Mar 22, 2023
@internet-diglett internet-diglett deleted the integrate-softnpu branch March 22, 2023 20:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
networking Related to the networking.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Remove the "external IP hack" Initial integration with Dendrite
5 participants