# qBittorrent UPnP and NAT-PMP Integration: Automatic Port Forwarding Implementation

> Learn how qBittorrent uses UPnP and NAT-PMP for automatic port forwarding. Understand the integration behind seamless network configuration.

- Repository: [qBittorrent project/qBittorrent](https://github.com/qbittorrent/qBittorrent)
- Tags: internals
- Published: 2026-05-05

---

**qBittorrent implements automatic port forwarding through a layered architecture where the UI and API interact with a singleton `Net::PortForwarder` class that delegates to `SessionImpl`, which configures libtorrent to handle UPnP and NAT-PMP protocols with routers.**

The qbittorrent/qBittorrent repository provides seamless router configuration via UPnP and NAT-PMP integration, eliminating manual port setup. This feature automatically requests the router to open listening ports for incoming peer connections. The implementation spans from the Qt-based desktop interface to the libtorrent networking core, offering consistent behavior across the GUI, Web UI, and REST API.

## Architecture Overview

qBittorrent’s UPnP and NAT-PMP integration follows a clean, layered design that isolates router-interaction logic from the rest of the client. The architecture ensures consistent state across the desktop application, Web UI, and programmatic interfaces.

### Layered Design

The system comprises five distinct layers:

1. **UI (Qt)** – Presents the *“Use UPnP / NAT‑PMP port forwarding from my router”* checkbox in `OptionsDialog` ([`src/gui/optionsdialog.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/src/gui/optionsdialog.cpp)) and forwards changes to the core.

2. **Web UI / API** – Exposes the UPnP state through `AppController` ([`src/webui/api/appcontroller.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/src/webui/api/appcontroller.cpp)), allowing remote clients to read and modify the setting via the `upnp` JSON key.

3. **PortForwarder Abstraction** – Provides a singleton interface (`Net::PortForwarder` in [`src/base/net/portforwarder.h`](https://github.com/qbittorrent/qBittorrent/blob/main/src/base/net/portforwarder.h)) used throughout the codebase for decoupled access.

4. **Concrete Implementation** – `PortForwarderImpl` ([`src/base/bittorrent/portforwarderimpl.h`](https://github.com/qbittorrent/qBittorrent/blob/main/src/base/bittorrent/portforwarderimpl.h)) inherits from the abstract class and forwards requests to the BitTorrent session.

5. **BitTorrent Session** – `SessionImpl` ([`src/base/bittorrent/sessionimpl.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/src/base/bittorrent/sessionimpl.cpp)) applies libtorrent settings and manages individual port mappings through `add_port_mapping()` and `delete_port_mapping()`.

### Data Flow

When a user toggles the UPnP checkbox or sends an API request:

1. `OptionsDialog` or `AppController` calls `Net::PortForwarder::instance()->setEnabled(bool)`.

2. `PortForwarderImpl::setEnabled()` stores the value in `m_storeActive` and invokes `start()` or `stop()`.

3. `PortForwarderImpl::start()` triggers `SessionImpl::enablePortMapping()`, which builds a `lt::settings_pack` with `enable_upnp = true` and `enable_natpmp = true`, applying it to the native libtorrent session.

4. When ports change, `SessionImpl::addMappedPorts()` calls `m_nativeSession->add_port_mapping(lt::session::tcp, port, port)` for each port, storing returned handles in `m_mappedPorts`.

5. Disabling UPnP triggers `disablePortMapping()`, which clears `m_mappedPorts` and sets libtorrent flags to false.

## Implementation Details

### The PortForwarder Singleton

The `Net::PortForwarder` class provides a global access point for port forwarding operations. Defined in [`src/base/net/portforwarder.h`](https://github.com/qbittorrent/qBittorrent/blob/main/src/base/net/portforwarder.h), it uses a singleton pattern to ensure only one instance manages router mappings:

```cpp
// src/base/net/portforwarder.h
class PortForwarder : public QObject
{
    Q_DISABLE_COPY_MOVE(PortForwarder)

public:
    explicit PortForwarder(QObject *parent = nullptr);
    ~PortForwarder() override;

    static PortForwarder *instance();

    virtual bool isEnabled() const = 0;
    virtual void setEnabled(bool enabled) = 0;
    virtual void setPorts(const QString &profile, QSet<quint16> ports) = 0;
    virtual void removePorts(const QString &profile) = 0;
};

```

The implementation in [`src/base/net/portforwarder.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/src/base/net/portforwarder.cpp) manages the static instance:

```cpp
// src/base/net/portforwarder.cpp
PortForwarder::PortForwarder(QObject *parent) : QObject {parent}
{
    Q_ASSERT(!m_instance);
    m_instance = this;
}
PortForwarder::~PortForwarder()
{
    m_instance = nullptr;
}
PortForwarder *PortForwarder::instance()
{
    return m_instance;
}
PortForwarder *PortForwarder::m_instance = nullptr;

```

### Concrete Implementation in PortForwarderImpl

`PortForwarderImpl` ([`src/base/bittorrent/portforwarderimpl.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/src/base/bittorrent/portforwarderimpl.cpp)) bridges the abstract interface with the libtorrent session. It maintains a cache of active port profiles and delegates all protocol operations to `SessionImpl`:

```cpp
// src/base/bittorrent/portforwarderimpl.cpp (excerpt)
PortForwarderImpl::PortForwarderImpl(BitTorrent::SessionImpl *provider, QObject *parent)
    : Net::PortForwarder(parent)
    , m_storeActive {u"Network/PortForwardingEnabled"_s, true}
    , m_provider {provider}
{
    if (isEnabled())
        start();
}

void PortForwarderImpl::start()
{
    m_provider->enablePortMapping();
    for (const QSet<quint16> &ports : asConst(m_portProfiles))
        m_provider->addMappedPorts(ports);
}

void PortForwarderImpl::stop()
{
    m_provider->disablePortMapping();
}

```

### Session-Level Mapping

The `SessionImpl` class in [`src/base/bittorrent/sessionimpl.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/src/base/bittorrent/sessionimpl.cpp) performs the actual UPnP/NAT-PMP configuration. The `enablePortMapping()` method activates both protocols in libtorrent:

```cpp
// src/base/bittorrent/sessionimpl.cpp (excerpt)
void SessionImpl::enablePortMapping()
{
    invokeAsync([this] {
        if (m_isPortMappingEnabled)
            return;

        lt::settings_pack settingsPack;
        settingsPack.set_bool(lt::settings_pack::enable_upnp, true);
        settingsPack.set_bool(lt::settings_pack::enable_natpmp, true);
        m_nativeSession->apply_settings(std::move(settingsPack));

        m_isPortMappingEnabled = true;
        LogMsg(tr("UPnP/NAT-PMP support: ON"), Log::INFO);
    });
}

```

To create individual mappings, `addMappedPorts()` iterates through the port set and stores the mapping handles:

```cpp
void SessionImpl::addMappedPorts(const QSet<quint16> &ports)
{
    invokeAsync([this, ports] {
        if (!m_isPortMappingEnabled)
            return;
        for (const quint16 port : ports)
            if (!m_mappedPorts.contains(port))
                m_mappedPorts.insert(port, m_nativeSession->add_port_mapping(lt::session::tcp, port, port));
    });
}

```

The **UPnP lease duration** setting (configured in the UI as *“UPnP lease duration [0: permanent lease]”*) is passed to libtorrent via `settings_pack::upnp_lease_duration` before applying settings.

### UI and API Integration

The desktop GUI wires the checkbox in `OptionsDialog` ([`src/gui/optionsdialog.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/src/gui/optionsdialog.cpp)):

```cpp
// src/gui/optionsdialog.cpp (excerpt)
m_ui->checkUPnP->setChecked(Net::PortForwarder::instance()->isEnabled());
connect(m_ui->checkUPnP, &QAbstractButton::toggled,
        this, &OptionsDialog::onUPnPToggled);

void OptionsDialog::onUPnPToggled(bool checked)
{
    Net::PortForwarder::instance()->setEnabled(checked);
}

```

The Web UI exposes the same functionality through `AppController` ([`src/webui/api/appcontroller.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/src/webui/api/appcontroller.cpp)):

```cpp
// src/webui/api/appcontroller.cpp (excerpt)
data[u"upnp"_s] = Net::PortForwarder::instance()->isEnabled();
// …
if (hasKey(u"upnp"_s))
    Net::PortForwarder::instance()->setEnabled(it.value().toBool());

```

## Practical Usage Examples

### Enabling Port Forwarding Programmatically

To enable automatic port forwarding in C++:

```cpp
// Enable UPnP/NAT-PMP
Net::PortForwarder::instance()->setEnabled(true);

// Configure specific ports for a profile
QSet<quint16> ports;
ports << 6881 << 6882;
Net::PortForwarder::instance()->setPorts(QStringLiteral("default"), ports);

```

### Disabling and Clearing Mappings

```cpp
// Disable and remove all mappings
Net::PortForwarder::instance()->setEnabled(false);

```

### Querying State via Web API

Retrieve the current UPnP status using curl:

```bash

# Get current status

curl -s http://localhost:8080/api/v2/app/preferences | jq .upnp

# Enable UPnP via API

curl -X POST -d "json={\"upnp\":true}" http://localhost:8080/api/v2/app/setPreferences

```

## Summary

- **qBittorrent UPnP and NAT-PMP integration** relies on a singleton `Net::PortForwarder` abstraction that decouples the UI from the networking core.
- **Implementation files**: [`src/base/net/portforwarder.h`](https://github.com/qbittorrent/qBittorrent/blob/main/src/base/net/portforwarder.h) defines the interface, while [`src/base/bittorrent/portforwarderimpl.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/src/base/bittorrent/portforwarderimpl.cpp) and [`src/base/bittorrent/sessionimpl.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/src/base/bittorrent/sessionimpl.cpp) handle libtorrent delegation.
- **Protocol activation**: `SessionImpl::enablePortMapping()` sets `enable_upnp` and `enable_natpmp` flags in the libtorrent settings pack.
- **Mapping lifecycle**: Ports are mapped via `add_port_mapping()` with handles stored in `m_mappedPorts`; cleanup occurs automatically on disable or shutdown.
- **Consistent API**: Both the Qt GUI (`OptionsDialog`) and Web UI (`AppController`) interact with the same singleton instance.

## Frequently Asked Questions

### How does qBittorrent handle UPnP lease duration?

The lease duration is configured through the UI field *“UPnP lease duration [0: permanent lease]”* and passed to libtorrent via `settingsPack.set_int(lt::settings_pack::upnp_lease_duration, duration)`. A value of `0` creates a permanent mapping, while positive values specify the lease lifetime in seconds.

### What happens to port mappings when qBittorrent shuts down?

During application shutdown, the `SessionImpl` destructor clears the `PortForwarder` singleton, which triggers `disablePortMapping()`. This invokes `delete_port_mapping(handle)` for all active handles stored in `m_mappedPorts`, ensuring routers remove the temporary port forwards.

### Can I enable UPnP/NAT-PMP via the Web API without using the GUI?

Yes. Send a POST request to `/api/v2/app/setPreferences` with a JSON payload containing `"upnp": true`. The `AppController` processes this in [`src/webui/api/appcontroller.cpp`](https://github.com/qbittorrent/qBittorrent/blob/main/src/webui/api/appcontroller.cpp) by calling `Net::PortForwarder::instance()->setEnabled(true)`, identical to the desktop checkbox behavior.

### Does qBittorrent differentiate between UPnP and NAT-PMP in the settings?

No. The qBittorrent implementation treats UPnP and NAT-PMP as a single feature flag. When enabled via `enablePortMapping()`, both `lt::settings_pack::enable_upnp` and `lt::settings_pack::enable_natpmp` are set to `true` simultaneously, allowing the client to use whichever protocol the router supports.