مشروع OWL و OpenDrop.
OWL (Open Wireless Link) هو التطبيق المرجعي مفتوح المصدر لـ AWDL على Linux، يعمل بالكامل في userspace. OpenDrop يجلس فوقه ليطبّق AirDrop. معاً يثبتان أن التوافقية الكاملة ممكنة على عتاد غير Apple — بشرط بطاقة Wi-Fi مناسبة.
المعمارية العامة لـ OWL
OWL يستغلّ واجهة Wi-Fi بقدرتي active monitor + injection، ويبني فوقها كل منطق AWDL برمجياً:
المكوّنات الستة
- Channel Hopping Engine: مؤقّت برمجي يبدّل قناة الواجهة وفق التسلسل المُعلَن، متزامناً مع نوافذ الـ AW.
- Synchronization & Election: يستمع لإطارات Sync من Master قائم؛ إن لم يسمع أحداً، يبدأ الانتخاب ببثّ إطارات Sync الخاصة به.
- Frame Processing: يلتقط الإطارات عبر packet sockets (
AF_PACKET)، يحلّل الـ TLVs و AWDL headers، ويبني الإطارات الصادرة ويحقنها. - ACK handling: لا يبني OWL إطارات ACK برمجياً؛ عتاد AR9280 في active monitor يؤكّد تلقائياً أي إطار موجّه لعنوان MAC الخاص بنسخة OWL.
- IPv6 عبر TUN: الحمولة المستخرجة تُمرَّر لمكدّس IPv6 في النظام عبر واجهة TUN، فتعمل بقيّة البروتوكولات (mDNS, TCP) طبيعياً.
- mDNS responder: يعلن خدمة AirDrop وتبادل الـ TXT records لبدء طبقة AirDrop.
المكوّن #4 (ACK) هو الذي يفسّر لماذا تحتاج بطاقة Atheros تحديداً. OWL لا يستطيع توليد ACK حتى لو أراد. هذه المهمة موكّلة للسيليكون. كل ما يفعله OWL هو ضبط عنوان MAC للواجهة حتى يعرف العتاد متى يجب أن يؤكّد.
محرك القفز بين القنوات — تفاصيل التنفيذ
- يقرأ OWL Channel Sequence TLV من إطارات الـ Master ليعرف جدول القنوات الفعلي (لا يفترضه).
- يضبط مؤقّتاً عالي الدقّة (high-resolution timer) ليبدّل القناة عبر
nl80211/iwعند بداية كل AW. - التحدّي: انجراف الساعة (clock drift) وكمون تبديل القناة (channel switch latency). تبديل القناة ليس فورياً، وإن تأخّر فسيفوّت بداية النافذة. لذا يعدّل OWL باستمرار وفق توقيت الـ Master (TSF).
channel_hop.c · simplified loop
/* read schedule from latest Master frame */
uint8_t schedule[16]; // 16 slots, one channel each
uint64_t master_tsf; // reference time from Master
while (running) {
uint64_t now = get_local_tsf();
uint32_t aw_dur = 16 * 1024; // 16 TU in µs
uint32_t idx = ((now - master_tsf) / aw_dur) % 16;
uint8_t ch = schedule[idx];
iw_set_channel("mon0", ch);
uint64_t next_boundary = master_tsf
+ (((now - master_tsf) / aw_dur) + 1) * aw_dur;
sleep_until(next_boundary - SWITCH_LATENCY_FUDGE);
}
آلة حالة المزامنة والانتخاب — في الكود
تنفيذ مبسّط لمنطق الوحدة 1.6:
election_fsm.c · core loop
enum state { LISTENING, SLAVE, MASTER } s = LISTENING;
while (running) {
frame_t f = recv_frame(); // blocks with timeout
if (f.type == PSF || f.type == MIF) {
peer_t p = parse_election_tlv(&f);
if (p.metric > my_metric) {
/* adopt their timing, follow them */
adjust_tsf(p.tsf);
adopt_channel_seq(p.channel_seq_tlv);
s = SLAVE;
last_heard = now();
}
}
if (now() - last_heard > MASTER_TIMEOUT) {
/* no master heard recently — claim the throne */
s = MASTER;
broadcast_psf();
}
if (s == MASTER && psf_due()) {
broadcast_psf(); // every ~110 ms
}
update_sync_tree();
sleep_until(next_event());
}
تحليل وبناء الإطارات
التحليل (parsing)
- فكّ ترويسة 802.11 → التحقق من
BSSID == 00:25:00:FF:94:73 - فكّ Action frame والـ OUI =
00:17:F2 - المرور على الـ TLVs واحداً واحداً (type / length / value)
- تخزين القيم في بنية state للجار (peer)
البناء (building)
عكس العملية — تركيب ترويسة MAC + Action + الـ TLVs المطلوبة (Sync/Election/Channel Seq)، أو لإطار بيانات: MAC + AWDL data header + حمولة IPv6.
يجب احترام endianness وحقول الطول بدقّة؛ خطأ بايت واحد = إطار يُرفض بصمت من الـ peer دون أي رسالة خطأ.
tlv_parse.c · linear TLV walk
int parse_tlvs(const uint8_t *buf, size_t len, peer_t *p) {
size_t off = 0;
while (off + 3 <= len) {
uint8_t type = buf[off];
uint16_t tlv_len = le16(&buf[off + 1]);
const uint8_t *val = &buf[off + 3];
if (off + 3 + tlv_len > len) return -1; /* malformed */
switch (type) {
case 0x04: parse_sync_params(val, tlv_len, p); break;
case 0x05: parse_election(val, tlv_len, p); break;
case 0x12: parse_channel_sequence(val, tlv_len, p); break;
case 0x02: parse_service_response(val, tlv_len, p); break;
/* … other TLVs … */
default: /* unknown — skip, forward-compat */ break;
}
off += 3 + tlv_len;
}
return 0;
}
OpenDrop — AirDrop في userspace
OpenDrop (بايثون) يجلس فوق رابط IPv6 الذي يوفّره OWL، ويطبّق مكدّس الوحدة 2:
- يعلن/يكتشف خدمة
_airdrop._tcp.local.عبر mDNS. - يشغّل/يخاطب خادم HTTPS (mTLS) عبر نقاط
/Discover,/Ask,/Upload. - يبني/يفكّ أرشيف cpio المضغوط للملفات.
- القيود: يعمل بأفضل حالاته مع وضع Everyone؛ التوافق الكامل مع Contacts Only أصعب بكثير لاعتماده على شهادات Apple.
البناء والتشغيل (نظرة عملية)
تسلسل التشغيل النموذجي:
root@workstation: ~ · bringing OWL + OpenDrop up
# 1) متطلبات
$ sudo apt install libpcap-dev libnl-3-dev libnl-genl-3-dev cmake build-essential
$ pip install opendrop # أو من المصدر
# 2) جهّز واجهة active monitor (مثال ath9k)
$ sudo iw dev wlan0 del
$ sudo iw phy phy0 interface add mon0 type monitor flags active
$ sudo ip link set mon0 up
# 3) شغّل OWL على الواجهة (ينشئ awdl0 افتراضياً)
$ sudo owl -i mon0
[awdl] starting on channel 44
[awdl] BSSID = 00:25:00:ff:94:73
[awdl] no master heard, becoming MASTER
[awdl] heard peer a4:83:e7:… metric=510 (higher)
[awdl] demoting to SLAVE, syncing TSF
[awdl] awdl0 up — fe80::a8bb:ccff:fe11:2233
# 4) في نافذة أخرى، شغّل OpenDrop فوق واجهة awdl0
$ opendrop find
Looking for receivers. Press Ctrl+C to stop ...
Found index=0 ID=4F2A19BB name="Khaled's iPhone"
$ opendrop send -r 0 -f file.jpg
Asking receiver to accept ...
Receiver accepted
Uploading file ...
Uploading has been successful
هذه أوامر توضيحية للمسار العام؛ الأسماء والأعلام تتغيّر بين الإصدارات. اعتمد README الرسمي للمشروع كمصدر نهائي.
نقاط ارتكاز الوحدة 4
- OWL ينفّذ AWDL كاملاً في userspace فوق بطاقة active-monitor+injection.
- لا يولّد ACK برمجياً — العتاد يفعل ذلك تلقائياً (السبب الجذري لأهمية اختيار البطاقة).
- يلتقط/يحقن عبر
AF_PACKET، ويجسر للـ IPv6 عبر TUN. - OpenDrop يبني AirDrop فوق رابط OWL، ويبدع في وضع Everyone.
تمارين الوحدة 4
- لماذا يستخدم OWL واجهة TUN بدل تنفيذ مكدّس IPv6 من الصفر؟ ما الذي يكسبه من النواة؟
- حدّد في معمارية OWL: أي مكوّن يكسر إذا انجرفت ساعتك المحلية عن الـ Master؟ ولماذا؟
- لماذا يكتفي OpenDrop عملياً بوضع Everyone؟ اربط بـ الوحدة 2.6.