diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8599aaa..54f41a3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -16,6 +16,6 @@ set(MAIN_SOURCE_FILES
src/ble.cpp)
add_executable(asteroid-tap2ble ${MAIN_SOURCE_FILES})
-target_link_libraries(asteroid-tap2ble resolv Qt5::Core Qt5::DBus)
+target_link_libraries(asteroid-tap2ble Qt5::Core Qt5::DBus)
install(TARGETS asteroid-tap2ble DESTINATION bin)
diff --git a/README.md b/README.md
index 18f8c44..a255218 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,16 @@
-# asteroid-tap2ble
+asteroid-tap2ble
+================
This daemon creates a TAP interface and exposes a BLE service with a RX and a
TX characteristic. These map to read/write operations on that TAP interface.
-The protocol takes into account fragmentation due to the BLE MTU so each BLE
-message is prepended with a one-byte header that contains a sequence number (to
-drop missed messages) and a bit to specify the end of a message.
+D-Bus forwarding
+----------------
-This effectively exposes IP connectivity from the watch to companion apps where
-this TAP traffic can be injected as RAW sockets or fed to daemons like passt.
+To expose D-Bus to the companion:
+
+1. Add a line containing `ListenStream=55556` right above the other
+ `ListenStream=…` in `/usr/lib/systemd/user/dbus.socket`
+2. Enable anonymous authentication in `/usr/share/dbus-1/session.conf`
+ by adding `` and `ANONYMOUS`
+ to the ``
diff --git a/asteroid-tap2ble.service b/asteroid-tap2ble.service
index 777b3a5..7a241fd 100644
--- a/asteroid-tap2ble.service
+++ b/asteroid-tap2ble.service
@@ -1,14 +1,16 @@
[Unit]
Description=Starts the AsteroidOS IP over BLE connectivity daemon
Requires=dbus.socket
-After=bluetooth.service
+After=connman.service
ConditionUser=root
-
+
[Service]
Type=simple
ExecStart=/usr/bin/asteroid-tap2ble
Restart=always
-
+User=ceres
+Group=ceres
+
[Install]
WantedBy=default.target
diff --git a/src/ble-dbus.cpp b/src/ble-dbus.cpp
index 381e669..43cdc1a 100644
--- a/src/ble-dbus.cpp
+++ b/src/ble-dbus.cpp
@@ -18,10 +18,22 @@
#include "ble-dbus.h"
// Called when a companion writes to the RX characteristic
-void RXChrc::WriteValue(QByteArray data, QVariantMap) {
+void RXChrc::WriteValue(QByteArray data, QVariantMap properties) {
+ if (properties.contains("mtu")) {
+ emit mtuChanged(properties["mtu"].toUInt());
+ } else
+ qWarning() << "mtu not in WriteValue";
emit receivedFromCompanion(data);
}
+QByteArray TXChrc::ReadValue(QVariantMap properties) {
+ if (properties.contains("mtu")) {
+ emit mtuChanged(properties["mtu"].toUInt());
+ } else
+ qWarning() << "mtu not in ReadValue";
+ return m_value;
+}
+
// Forwards information to the companion by notifications on the TX characteristic
void TXChrc::sendToCompanion(QByteArray content) {
m_value = content;
diff --git a/src/ble-dbus.h b/src/ble-dbus.h
index a16988c..a905504 100644
--- a/src/ble-dbus.h
+++ b/src/ble-dbus.h
@@ -34,9 +34,9 @@
#define TX_PATH "/org/asteroidos/tap2ble/service/tx"
#define RX_PATH "/org/asteroidos/tap2ble/service/rx"
-#define SERVICE_UUID "00001071-0000-0000-0000-00A57E401D05"
-#define RX_UUID "00001001-0000-0000-0000-00A57E401D05"
-#define TX_UUID "00001002-0000-0000-0000-00A57E401D05"
+#define SERVICE_UUID "0000A071-0000-0000-0000-00A57E401D05"
+#define RX_UUID "0000A001-0000-0000-0000-00A57E401D05"
+#define TX_UUID "0000A002-0000-0000-0000-00A57E401D05"
// Writable characteristic for companion to watch communication
class RXChrc : public QObject
@@ -62,6 +62,7 @@ public slots:
signals:
void receivedFromCompanion(const QByteArray &);
+ void mtuChanged(int);
};
// Notifiable characteristic for watch to companion communication
@@ -86,12 +87,13 @@ public slots:
void WriteValue(QByteArray value, QVariantMap options) {}
void StartNotify() {}
void StopNotify() {}
- QByteArray ReadValue(QVariantMap) { return m_value; }
+ QByteArray ReadValue(QVariantMap);
void sendToCompanion(QByteArray content);
signals:
void valueChanged();
+ void mtuChanged(int);
private:
void emitPropertiesChanged() {
diff --git a/src/ble.cpp b/src/ble.cpp
index 8961c32..b220682 100644
--- a/src/ble.cpp
+++ b/src/ble.cpp
@@ -35,6 +35,8 @@
#define GATT_DESC_IFACE "org.bluez.GattDescriptor1"
BLE::BLE(QObject *parent) : QObject(parent), mBus(QDBusConnection::systemBus()) {
+ mCurrentMtu = -1;
+
qDBusRegisterMetaType();
qDBusRegisterMetaType();
@@ -59,6 +61,9 @@ BLE::BLE(QObject *parent) : QObject(parent), mBus(QDBusConnection::systemBus())
connect(&mRX, SIGNAL(receivedFromCompanion(QByteArray)), this, SLOT(onReceivedFromCompanion(QByteArray)));
+ connect(&mRX, SIGNAL(mtuChanged(int)), this, SLOT(onMtuChanged(int)));
+ connect(&mTX, SIGNAL(mtuChanged(int)), this, SLOT(onMtuChanged(int)));
+
QDBusInterface remoteOm(BLUEZ_SERVICE_NAME, "/", DBUS_OM_IFACE, mBus);
if (remoteOm.isValid())
bluezServiceRegistered(BLUEZ_SERVICE_NAME);
@@ -163,47 +168,17 @@ void BLE::onConnectedChanged() {
}
}
-// TODO: This should be determined dynamically based on the BLE MTU
-#define CHUNK_SIZE 240
-
void BLE::sendToCompanion(const QByteArray &content) {
- uint8_t seqNum = 0;
- int currentIndex = 0;
-
- while (currentIndex < content.size()) {
- // The header is one bit of "there are more chunks" and 7 bits of
- // sequence number to detect dropped messages
- uint8_t header = (seqNum & 0x7F) |
- ((currentIndex + CHUNK_SIZE-1) < content.size() ? 0x80 : 0);
- QByteArray headerBA((char*)&header, sizeof(header));
-
- mTX.sendToCompanion(headerBA + content.mid(currentIndex, CHUNK_SIZE - 1));
-
- currentIndex += CHUNK_SIZE - 1;
- seqNum++;
- }
+ mTX.sendToCompanion(content);
}
void BLE::onReceivedFromCompanion(const QByteArray &content) {
- static int8_t lastSeqNum = -1;
-
- uint8_t header = content.at(0);
- bool hasMore = !!(header & 0x80);
- uint8_t seqNum = header & 0x7F;
-
- if (seqNum != lastSeqNum + 1) {
- mAccumulatedRecv = QByteArray();
- lastSeqNum = -1;
- return;
- }
-
- lastSeqNum = seqNum;
- mAccumulatedRecv.append(content.mid(1, -1));
+ emit receivedFromCompanion(content);
+}
- if (!hasMore) {
- emit receivedFromCompanion(mAccumulatedRecv);
- mAccumulatedRecv = QByteArray();
- lastSeqNum = -1;
+void BLE::onMtuChanged(int mtu) {
+ if (mCurrentMtu != mtu) {
+ mCurrentMtu = mtu;
+ emit mtuChanged(mtu - 3);
}
}
-
diff --git a/src/ble.h b/src/ble.h
index 14c0601..881159c 100644
--- a/src/ble.h
+++ b/src/ble.h
@@ -47,17 +47,18 @@ class BLE : public QObject
QString mAdapter;
bool mConnected;
+ int mCurrentMtu;
+
void updateAdapter();
void setAdapter(QString adatper);
void setConnected(bool connected);
- QByteArray mAccumulatedRecv;
-
signals:
void connectedChanged();
void adapterChanged();
void receivedFromCompanion(const QByteArray &data);
+ void mtuChanged(int mtu);
public slots:
void bluezServiceRegistered(const QString &name);
@@ -68,6 +69,8 @@ public slots:
void onConnectedChanged();
void onAdapterChanged();
+ void onMtuChanged(int);
+
private slots:
void onReceivedFromCompanion(const QByteArray &data);
};
diff --git a/src/main.cpp b/src/main.cpp
index 25c8f9d..2b65a7f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -25,6 +25,7 @@ int main(int argc, char *argv[]) {
TAP tap;
BLE ble;
+ QObject::connect(&ble, &BLE::mtuChanged, &tap, &TAP::mtuChanged);
QObject::connect(&tap, &TAP::dataAvailable, &ble, &BLE::sendToCompanion);
QObject::connect(&ble, &BLE::receivedFromCompanion, &tap, &TAP::send);
diff --git a/src/tap.cpp b/src/tap.cpp
index 23e2caf..a3a445d 100644
--- a/src/tap.cpp
+++ b/src/tap.cpp
@@ -18,14 +18,21 @@
#include "tap.h"
#include
+#include
+#include
+#include
+#include
#include
#include
#include
#include
+#include
#include
#include
+#define MY_ETH_TAP_HARD_FRAME_SIZE (14)
+
TAP::TAP(QObject *parent) : QObject(parent) {
// Create the interface
mFd = open("/dev/net/tun", O_RDWR);
@@ -41,27 +48,135 @@ TAP::TAP(QObject *parent) : QObject(parent) {
close(mFd);
exit(1);
}
+ if (ioctl(mFd, TUNSETOFFLOAD, 0U) < 0) {
+ qCritical() << "Failed to set the TAP offload";
+ }
+
+ mIfaceName = ifr.ifr_name;
+ qDebug() << "TAP interface created:" << mIfaceName;
- QString ifaceName = ifr.ifr_name;
- qDebug() << "TAP interface created:" << ifaceName;
+ iffReset(244);
- // Bring the interface up
- if (QProcess::execute("ip", {"link", "set", "dev", ifaceName, "up"}) != 0) {
- qCritical() << "Failed to bring up TAP interface";
+ char discard[1500];
+ auto drained = read(mFd, discard, 1500);
+ qDebug() << "Drained" << drained << "bytes";
+
+ // Poll the interface file descriptor
+ mNotifier = new QSocketNotifier(mFd, QSocketNotifier::Read, this);
+ QObject::connect(mNotifier, &QSocketNotifier::activated, this, &TAP::fdActivated);
+}
+
+void TAP::iffReset(int mtu) {
+ struct ifreq ifr = {};
+ auto sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ qCritical() << "Failed to create a socket";
exit(1);
}
- qDebug() << "TAP interface" << ifaceName << "is up";
-
- // Configure routing tables
- if (QProcess::execute("ip", {"route", "add", "default", "dev", ifaceName}) != 0) {
- qCritical() << "Failed to configure routing table";
+ strncpy(ifr.ifr_name, mIfaceName.toLocal8Bit().data(), sizeof(ifr.ifr_name));
+ if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
+ qCritical() << "Failed to get IFFLAGS";
+ exit(1);
+ }
+ if (ifr.ifr_flags & IFF_UP) {
+ ifr.ifr_flags &= ~IFF_UP;
+ if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0) {
+ qCritical() << "Failed to set IFFLAGS";
+ exit(1);
+ }
+ }
+ if (mtu < 0) {
+ qDebug() << "Interface down";
+ close(sock);
+ return;
+ }
+ ifr.ifr_flags |= IFF_UP;
+ if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0) {
+ qCritical() << "Failed to set IFFLAGS";
exit(1);
}
- qDebug() << "Routing table configured to send all traffic to" << ifaceName;
- // Poll the interface file descriptor
- mNotifier = new QSocketNotifier(mFd, QSocketNotifier::Read, this);
- QObject::connect(mNotifier, &QSocketNotifier::activated, this, &TAP::fdActivated);
+ ifr.ifr_mtu = mtu - MY_ETH_TAP_HARD_FRAME_SIZE;
+ if (ioctl(sock, SIOCSIFMTU, &ifr) < 0) {
+ qCritical() << "Failed to set the MTU";
+ exit(1);
+ }
+
+ close(sock);
+
+ QDBusConnection bus = QDBusConnection::systemBus();
+ QDBusInterface manager("net.connman", "/", "net.connman.Manager", bus);
+ QDBusMessage result = manager.call("GetServices");
+
+ const QDBusArgument argument = result.arguments().at(0).value();
+ QMap reply;
+ argument >> reply;
+
+ for (auto it = reply.keyValueBegin(); it != reply.keyValueEnd(); ++it) {
+ const auto &key = it->first;
+ const auto &value = it->second;
+
+ if (value.contains("Ethernet")) {
+ //QString name = value["Ethernet"].toMap()["Name"].toString();
+ QDBusArgument ethernetArg = value["Ethernet"].value();
+ QVariantMap ethernetMap;
+ ethernetArg >> ethernetMap;
+
+ const auto name = ethernetMap["Interface"].toString();
+
+ if (!strcmp(mIfaceName.toLocal8Bit().data(), name.toLocal8Bit().data())) {
+ qDebug() << "Will reset the IPv4 configuration";
+
+ QDBusInterface service("net.connman", key.path(), "net.connman.Service", bus);
+
+ {
+ QDBusArgument propertiesArg = value["IPv4.Configuration"].value();
+ QVariantMap propertiesMap;
+ propertiesArg >> propertiesMap;
+
+ propertiesMap["Method"] = "manual";
+ propertiesMap["Address"] = "10.0.2.3";
+ propertiesMap["Netmask"] = "255.255.255.0";
+ propertiesMap["Gateway"] = "10.0.2.2";
+
+ QVariantList args;
+ args << "IPv4.Configuration" << QVariant::fromValue(QDBusVariant(propertiesMap));
+ service.callWithArgumentList(QDBus::AutoDetect, "SetProperty", args);
+ }
+
+ {
+ QStringList propertiesMap;
+
+ propertiesMap << "10.0.2.2";
+ // TODO:XXX: CHANGE THIS!!!
+ propertiesMap << "8.8.4.4";
+ propertiesMap << "8.8.8.8";
+
+ QVariantList args;
+ args << "Nameservers.Configuration" << QVariant::fromValue(QDBusVariant(propertiesMap));
+ service.callWithArgumentList(QDBus::AutoDetect, "SetProperty", args);
+ }
+ }
+ }
+ }
+}
+
+void TAP::mtuChanged(int mtu) {
+ struct ifreq ifr = {};
+ auto sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ qCritical() << "Failed to create a socket";
+ exit(1);
+ }
+ strncpy(ifr.ifr_name, mIfaceName.toLocal8Bit().data(), sizeof(ifr.ifr_name));
+ ifr.ifr_mtu = mtu - MY_ETH_TAP_HARD_FRAME_SIZE;
+ if (ioctl(sock, SIOCSIFMTU, &ifr) < 0) {
+ qCritical() << "Failed to set the MTU";
+ exit(1);
+ }
+ close(sock);
+
+ qDebug() << "MTU reset to" << mtu;
}
// Called every time the watch kernel outputs ethernet frames to the network
@@ -71,8 +186,8 @@ void TAP::fdActivated() {
if (bytesRead > 0) {
QByteArray data(buffer, bytesRead);
- qDebug() << "Received" << bytesRead << "bytes from TAP interface";
- qDebug() << "Data:" << data.toHex();
+ // qDebug() << "Received" << bytesRead << "bytes from TAP interface";
+ // qDebug() << "Data:" << data.toHex();
emit dataAvailable(data);
} else
@@ -84,8 +199,8 @@ void TAP::send(const QByteArray &data) {
qint64 bytesWritten = write(mFd, data.constData(), data.size());
if (bytesWritten > 0) {
- qDebug() << "Sent" << bytesWritten << "bytes to the TAP interface";
- qDebug() << "Data:" << data.toHex();
+ // qDebug() << "Sent" << bytesWritten << "bytes to the TAP interface";
+ // qDebug() << "Data:" << data.toHex();
} else
qCritical() << "Failed to write to TAP interface";
}
diff --git a/src/tap.h b/src/tap.h
index e00df7d..59550cd 100644
--- a/src/tap.h
+++ b/src/tap.h
@@ -27,6 +27,10 @@ class TAP : public QObject
public:
explicit TAP(QObject *parent = 0);
void send(const QByteArray &data);
+ void mtuChanged(int mtu);
+
+private:
+ void iffReset(int mtu);
signals:
void dataAvailable(const QByteArray &data);
@@ -35,6 +39,7 @@ private slots:
void fdActivated();
private:
+ QString mIfaceName;
QSocketNotifier *mNotifier;
int mFd;
};