packet(7)

الاسم
packet - واجهة الحزمة على مستوى الجهاز

موجز
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <net/ethernet.h> /* the L2 protocols */

packet_socket = socket(AF_PACKET, int socket_type, int protocol);

الوصف
تُستخدم مقابس الحزمة لاستقبال أو إرسال حزم خام على مستوى برنامج تشغيل الجهاز (الطبقة الثانية من OSI). تسمح للمستخدم بتنفيذ وحدات البروتوكول في مساحة المستخدم فوق الطبقة المادية.

socket_type إما SOCK_RAW للحزم الخام بما في ذلك رأس طبقة الوصلة أو SOCK_DGRAM للحزم المطبوخة مع إزالة رأس طبقة الوصلة. تتوفر معلومات رأس طبقة الوصلة بتنسيق شائع في بنية sockaddr_ll. protocol هو رقم بروتوكول IEEE 802.3 بترتيب بايت الشبكة. راجع ملف التضمين <linux/if_ether.h> للحصول على قائمة البروتوكولات المسموح بها. عند تعيين البروتوكول إلى htons(ETH_P_ALL)، تُستقبل جميع البروتوكولات. تُمرر جميع الحزم الواردة من ذلك النوع البروتوكولي إلى مقبس الحزمة قبل تمريرها إلى البروتوكولات المنفذة في النواة. إذا تم تعيين protocol إلى صفر، لا تُستقبل أي حزم. يمكن استدعاء bind(2) اختياريًا مع sll_protocol غير صفري لبدء استقبال حزم للبروتوكولات المحددة.

لإنشاء مقبس حزمة، يجب أن تمتلك العملية القدرة CAP_NET_RAW في مساحة المستخدم التي تحكم مساحة الشبكة الخاصة بها.

تُمرر حزم SOCK_RAW إلى برنامج تشغيل الجهاز ومنه دون أي تغييرات في بيانات الحزمة. عند استقبال حزمة، يُحلل العنوان ويُمرر في بنية عنوان sockaddr_ll قياسية. عند إرسال حزمة، يجب أن يحتوي المخزن المؤقت المقدم من المستخدم على رأس الطبقة المادية. ثم تُصطف تلك الحزمة دون تعديل إلى برنامج تشغيل الشبكة للواجهة المحددة بواسطة العنوان الوجهة. تضيف بعض برامج تشغيل الأجهزة دائمًا رؤوسًا أخرى. SOCK_RAW مشابه لـ AF_INET/SOCK_PACKET القديم في Linux 2.0 ولكنه غير متوافق معه.

يعمل SOCK_DGRAM على مستوى أعلى قليلاً. يُزال الرأس المادي قبل تمرير الحزمة إلى المستخدم. تحصل الحزم المرسلة عبر مقبس حزمة SOCK_DGRAM على رأس طبقة مادية مناسب بناءً على المعلومات في عنوان الوجهة sockaddr_ll قبل أن تُصطف.

افتراضيًا، تُمرر جميع الحزم من نوع البروتوكول المحدد إلى مقبس حزمة. للحصول على حزم من واجهة محددة فقط، استخدم bind(2) مع تحديد عنوان في struct sockaddr_ll لربط مقبس الحزمة بواجهة. الحقول المستخدمة للربط هي sll_family (يجب أن يكون AF_PACKET) و sll_protocol و sll_ifindex.

عملية connect(2) غير مدعومة على مقابس الحزمة.

عند تمرير العلم MSG_TRUNC إلى recvmsg(2) أو recv(2) أو recvfrom(2)، يُعاد دائمًا الحجم الحقيقي للحزمة على السلك، حتى عندما يكون أطول من المخزن المؤقت.

  أنواع العناوين
بنية sockaddr_ll هي عنوان طبقة مادية مستقل عن الجهاز.

    struct sockaddr_ll {
        unsigned short sll_family;   /* Always AF_PACKET */
        unsigned short sll_protocol; /* Physical-layer protocol */
        int            sll_ifindex;  /* Interface number */
        unsigned short sll_hatype;   /* ARP hardware type */
        unsigned char  sll_pkttype;  /* Packet type */
        unsigned char  sll_halen;    /* Size of address */
        unsigned char  sll_addr[8];  /* Physical-layer address */
    };

حقول هذا الهيكل هي كالتالي:

sll_protocol
    هو نوع بروتوكول الإيثرنت القياسي بترتيب بايت الشبكة كما هو معرف في ملف التضمين <linux/if_ether.h>. المبدئي هو بروتوكول المقبس.

sll_ifindex
    هو فهرس الواجهة (انظر netdevice(7))؛ 0 يطابق أي واجهة (مسموح به فقط للربط). sll_hatype هو نوع ARP كما هو معرف في ملف التضمين <linux/if_arp.h>.

sll_pkttype
    يحتوي على نوع الحزمة. الأنواع الصالحة هي PACKET_HOST لحزمة موجهة إلى المضيف المحلي، PACKET_BROADCAST لحزمة بث على الطبقة المادية، PACKET_MULTICAST لحزمة مرسلة إلى عنوان متعدد الإرسال على الطبقة المادية، PACKET_OTHERHOST لحزمة إلى مضيف آخر تم التقاطها بواسطة برنامج تشغيل جهاز في وضع غير انتقائي، و PACKET_OUTGOING لحزمة صادرة من المضيف المحلي تُعاد حلقياً إلى مقبس حزمة. هذه الأنواع منطقية فقط للاستقبال.

sll_addr
sll_halen
    يحتويان على عنوان الطبقة المادية (مثل IEEE 802.3) وحجمه. يعتمد التفسير الدقيق على الجهاز.

عند إرسال الحزم، يكفي تحديد sll_family و sll_addr و sll_halen و sll_ifindex و sll_protocol. يجب أن تكون الحقول الأخرى 0. تُضبط sll_hatype و sll_pkttype على الحزم المستلمة لمعلوماتك.

  خيارات المقبس
تُهيأ خيارات مقبس الرزم باستدعاء setsockopt(2) بمستوى SOL_PACKET.

PACKET_ADD_MEMBERSHIP
PACKET_DROP_MEMBERSHIP
    يمكن استخدام مقابس الرزم لتهيئة البث المتعدد على الطبقة المادية والوضع غير الانتقائي. يضيف PACKET_ADD_MEMBERSHIP ربطًا ويسقطه PACKET_DROP_MEMBERSHIP. يتوقع كلاهما بنية packet_mreq كوسيطة:

        struct packet_mreq {
            int            mr_ifindex;    /* interface index */
            unsigned short mr_type;       /* action */
            unsigned short mr_alen;       /* address size */
            unsigned char  mr_address[8]; /* physical-layer address */
        };

    يحتوي mr_ifindex على دليل الواجهة للواجهة التي يجب تغيير حالتها. يحدد حقل mr_type الإجراء الذي سيُنفذ. يُفعّل PACKET_MR_PROMISC استقبال جميع الرزم على وسيط مشترك (يُعرف غالبًا باسم "الوضع غير الانتقائي")، ويربط PACKET_MR_MULTICAST المقبس بمجموعة البث المتعدد على الطبقة المادية المحددة في mr_address و mr_alen، ويُعدّ PACKET_MR_ALLMULTI المقبس لاستقبال جميع رزم البث المتعدد الواصلة إلى الواجهة.

    بالإضافة إلى ذلك، يمكن استخدام ioctls التقليدية SIOCSIFFLAGS و SIOCADDMULTI و SIOCDELMULTI لنفس الغرض.

PACKET_AUXDATA (منذ لينكس 2.6.21)
    إذا فُعّل هذا الخيار الثنائي، يمرر مقبس الرزم بنية بيانات وصفية مع كل رزمة في حقل التحكم recvmsg(2). يمكن قراءة البنية باستخدام cmsg(3). تُعرف كالتالي:

        struct tpacket_auxdata {
            __u32 tp_status;
            __u32 tp_len;      /* packet size */
            __u32 tp_snaplen;  /* captured size */
            __u16 tp_mac;
            __u16 tp_net;
            __u16 tp_vlan_tci;
            __u16 tp_vlan_tpid; /* Since Linux 3.14; earlier, these
                                   were unused padding bytes */
        };

PACKET_FANOUT (منذ لينكس 3.1)
    لتوسيع نطاق المعالجة عبر الخيوط، يمكن لمقابس الرزم تشكيل مجموعة توزيع. في هذا الوضع، تُدرج كل رزمة مطابقة في مقبس واحد فقط في المجموعة. ينضم مقبس إلى مجموعة توزيع باستدعاء setsockopt(2) بمستوى SOL_PACKET وخيار PACKET_FANOUT. يمكن لكل مساحة اسم شبكة أن تحتوي على ما يصل إلى 65536 مجموعة مستقلة. يختار المقبس مجموعة بترميز المعرف في أول 16 بت من قيمة الخيار الصحيحة. ينشئ أول مقبس رزم ينضم إلى مجموعة ضمنيًا. للانضمام بنجاح إلى مجموعة موجودة، يجب أن يكون لمقابس الرزم اللاحقة نفس البروتوكول وإعدادات الجهاز ووضع التوزيع والأعلام (انظر أدناه). يمكن لمقابس الرزم مغادرة مجموعة توزيع فقط بإغلاق المقبس. تُحذف المجموعة عند إغلاق آخر مقبس.

    يدعم التوزيع خوارزميات متعددة لنشر الحركة المرورية بين المقابس، كالتالي:

    •   الوضع المبدئي، PACKET_FANOUT_HASH، يرسل الرزم من نفس التدفق إلى نفس المقبس للحفاظ على ترتيب كل تدفق. لكل رزمة، يختار مقبسًا بأخذ تجزئة تدفق الرزمة معامل عدد المقابس في المجموعة، حيث تجزئة التدفق هي تجزئة على عنوان طبقة الشبكة وحقول منفذ طبقة النقل الاختيارية.

    •   ينفذ وضع موازنة الحمل PACKET_FANOUT_LB خوارزمية round-robin.

    •   يختار PACKET_FANOUT_CPU المقبس بناءً على وحدة المعالجة المركزية التي وصلت عليها الرزمة.

    •   يعالج PACKET_FANOUT_ROLLOVER جميع البيانات على مقبس واحد، منتقلًا إلى التالي عندما يصبح أحدها متراكمًا.

    •   يختار PACKET_FANOUT_RND المقبس باستخدام مولد أرقام شبه عشوائي.

    •   يختار PACKET_FANOUT_QM (المتوفر منذ لينكس 3.14) المقبس باستخدام queue_mapping المسجل لـ skb المستلم.

    يمكن لوضعيات التوزيع أن تأخذ خيارات إضافية. يتسبب تجزئة IP في أن يكون للرزم من نفس التدفق تجزئات تدفق مختلفة. العلم PACKET_FANOUT_FLAG_DEFRAG، إذا ضُبط، يتسبب في إزالة تجزئة الرزم قبل تطبيق التوزيع، للحفاظ على الترتيب حتى في هذه الحالة. يُبلغ عن وضع التوزيع والخيارات في الـ 16 بت الثانية من قيمة الخيار الصحيحة. يُفعّل العلم PACKET_FANOUT_FLAG_ROLLOVER آلية التجاوز كاستراتيجية احتياطية: إذا اختارت خوارزمية التوزيع الأصلية مقبسًا متراكمًا، تتجاوز الرزمة إلى المقبس التالي المتاح.

PACKET_LOSS (مع PACKET_TX_RING)
    عند مواجهة رزمة مشوهة على حلقة إرسال، المبدئي هو إعادة تعيين tp_status الخاص بها إلى TP_STATUS_WRONG_FORMAT وإحباط الإرسال فورًا. تمنع الرزمة المشوهة نفسها والرزم المدرجة لاحقًا من الإرسال. يجب إصلاح خطأ التنسيق، وإعادة تعيين tp_status المرتبط إلى TP_STATUS_SEND_REQUEST، وإعادة بدء عملية الإرسال عبر send(2). ومع ذلك، إذا ضُبط PACKET_LOSS، ستُتجاوز أي رزمة مشوهة، ويُعاد تعيين tp_status الخاص بها إلى TP_STATUS_AVAILABLE، وتستمر عملية الإرسال.

PACKET_RESERVE (مع PACKET_RX_RING)
    مبدئيًا، تكتب حلقة استقبال الرزم الرزم مباشرة بعد بنية البيانات الوصفية وحشو المحاذاة. يحتفظ هذا الخيار الصحيح بمساحة رأس إضافية.

PACKET_RX_RING
    إنشاء مخزن مؤقت حلقي مرتبط بالذاكرة لاستقبال الرزم غير المتزامن. يحجز مقبس الرزم منطقة متجاورة من مساحة عنوان التطبيق، ويرتبها في مصفوفة من فتحات الرزم، وينسخ الرزم (حتى tp_snaplen) في الفتحات المتعاقبة. تسبق كل رزمة بنية بيانات وصفية مشابهة لـ tpacket_auxdata. ترمز حقول البروتوكول إلى الإزاحة للبيانات من بداية الرأس الوصفي. يخزن tp_net الإزاحة لطبقة الشبكة. إذا كان مقبس الرزم من النوع SOCK_DGRAM، فإن tp_mac يكون مماثلاً. إذا كان من النوع SOCK_RAW، فإن هذا الحقل يخزن الإزاحة لإطار طبقة الوصلة. يتواصل مقبس الرزم والتطبيق حول بداية ونهاية الحلقة عبر حقل tp_status. يمتلك مقبس الرزم جميع الفتحات التي تكون قيمة tp_status فيها مساوية لـ TP_STATUS_KERNEL. بعد ملء فتحة، يغير حالة الفتحة لنقل الملكية إلى التطبيق. أثناء التشغيل العادي، تحتوي قيمة tp_status الجديدة على الأقل على بت TP_STATUS_USER مضبوط للإشارة إلى تخزين رزمة مستلمة. عندما ينهي التطبيق معالجة رزمة، ينقل ملكية الفتحة مرة أخرى إلى المقبس بضبط tp_status مساوياً لـ TP_STATUS_KERNEL.

    تنفذ مقابس الرزم متغيرات متعددة للحلقة الرزمة. تفاصيل التنفيذ موصوفة في Documentation/networking/packet_mmap.rst في شجرة مصدر نواة لينكس.

PACKET_STATISTICS
    استرداد إحصائيات مقبس الرزم في شكل بنية

        struct tpacket_stats {
            unsigned int tp_packets;  /* Total packet count */
            unsigned int tp_drops;    /* Dropped packet count */
        };

    استلام الإحصائيات يعيد ضبط العدادات الداخلية. تختلف بنية الإحصائيات عند استخدام حلقة من المتغير TPACKET_V3.

PACKET_TIMESTAMP (مع PACKET_RX_RING؛ منذ لينكس 2.6.36)
    تخزن حلقة استقبال الرزم دائمًا طابعًا زمنيًا في الرأس الوصفي. مبدئيًا، هذا طابع زمني مولّد برمجيًا عند نسخ الرزمة إلى الحلقة. يحدد هذا الخيار الصحيح نوع الطابع الزمني. بالإضافة إلى المبدئي، يدعم التنسيقين العتاديين الموصوفين في Documentation/networking/timestamping.rst في شجرة مصدر نواة لينكس.

PACKET_TX_RING (منذ لينكس 2.6.31)
    إنشاء مخزن مؤقت حلقي مرتبط بالذاكرة لإرسال الرزم. هذا الخيار مشابه لـ PACKET_RX_RING ويأخذ نفس الوسائط. يكتب التطبيق الرزم في فتحات تكون قيمة tp_status فيها مساوية لـ TP_STATUS_AVAILABLE ويجدولها للإرسال بتغيير tp_status إلى TP_STATUS_SEND_REQUEST. عندما تكون الرزم جاهزة للإرسال، يستدعي التطبيق send(2) أو متغيرًا منه. يتم تجاهل حقلي buf و len لهذه الاستدعاء. إذا تم تمرير عنوان باستخدام sendto(2) أو sendmsg(2)، فإنه يتجاوز المبدئي للمقبس. عند الإرسال الناجح، يعيد المقبس ضبط tp_status إلى TP_STATUS_AVAILABLE. يلغي الإرسال فورًا عند الخطأ ما لم يتم ضبط PACKET_LOSS.

PACKET_VERSION (مع PACKET_RX_RING؛ منذ لينكس 2.6.27)
    مبدئيًا، ينشئ PACKET_RX_RING حلقة استقبال رزم من المتغير TPACKET_V1. لإنشاء متغير آخر، قم بتكوين المتغير المطلوب بضبط هذا الخيار الصحيح قبل إنشاء الحلقة.

PACKET_QDISC_BYPASS (منذ لينكس 3.14)
    مبدئيًا، تمر الرزم المرسلة عبر مقابس الرزم عبر طبقة qdisc (التحكم في المرور) للنواة، وهو أمر جيد للغالبية العظمى من حالات الاستخدام. لأجهزة توليد المرور التي تستخدم مقابس الرزم والتي تهدف إلى إغراق الشبكة بالقوة—على سبيل المثال، لاختبار الأجهزة تحت الحمل بطريقة مشابهة لـ pktgen—يمكن تجاوز هذه الطبقة بضبط هذا الخيار الصحيح إلى 1. أحد الآثار الجانبية هو تجنب تخزين الرزم في طبقة qdisc، مما سيؤدي إلى زيادة الإسقاط عندما تكون قوائم إرسال جهاز الشبكة مشغولة؛ لذلك، استخدم على مسؤوليتك الخاصة.

  Ioctls
يمكن استخدام SIOCGSTAMP لاستلام الطابع الزمني لآخر رزمة مستلمة. الوسيطة هي متغير من نوع struct timeval.

بالإضافة إلى ذلك، جميع ioctls القياسية المعرفة في netdevice(7) و socket(7) صالحة على مقابس الرزم.

  معالجة الأخطاء
لا تقوم مقابس الرزم بأي معالجة للأخطاء بخلاف الأخطاء التي تحدث أثناء تمرير الرزمة إلى برنامج تشغيل الجهاز. ليس لديها مفهوم الخطأ المعلق.

الأخطاء

EADDRNOTAVAIL
    تم تمرير عنوان مجموعة بث متعدد غير معروف.

EFAULT
    مرر المستخدم عنوان ذاكرة غير صالح.

EINVAL
    معطى غير صالح.

EMSGSIZE
    الرزمة أكبر من MTU للواجهة.

ENETDOWN
    الواجهة غير نشطة.

ENOBUFS
    ذاكرة غير كافية لتخصيص الرزمة.

ENODEV
    اسم جهاز غير معروف أو فهرس واجهة محدد في عنوان الواجهة.

ENOENT
    لم يتم استلام أي رزمة.

ENOTCONN
    لم يتم تمرير أي عنوان واجهة.

ENXIO
    عنوان الواجهة احتوى على فهرس واجهة غير صالح.

EPERM
    المستخدم لا يملك صلاحيات كافية لتنفيذ هذه العملية.

بالإضافة إلى ذلك، قد تُولد أخطاء أخرى بواسطة برنامج التشغيل منخفض المستوى.

الإصدارات
AF_PACKET هي ميزة جديدة في لينكس 2.2. الإصدارات السابقة من لينكس دعمت فقط SOCK_PACKET.

ملاحظات
للبرامج المحمولة، يُقترح استخدام AF_PACKET عبر pcap(3)؛ على الرغم من أن هذا يغطي فقط مجموعة فرعية من ميزات AF_PACKET.

مقابس الحزم SOCK_DGRAM لا تقوم بأي محاولة لإنشاء أو تحليل رأس IEEE 802.2 LLC لإطار IEEE 802.3. عندما يُحدد ETH_P_802_3 كبروتوكول للإرسال، ينشئ النواة إطار 802.3 ويملأ حقل الحجم؛ يجب على المستخدم توفير رأس LLC للحصول على حزمة متوافقة تمامًا. حزم 802.3 الواردة لا تُضاعف على حقول بروتوكول DSAP/SSAP؛ بدلاً من ذلك، تُقدم للمستخدم كبروتوكول ETH_P_802_2 مع رأس LLC مسبوق. وبالتالي، لا يمكن الربط بـ ETH_P_802_3؛ اربط بـ ETH_P_802_2 بدلاً من ذلك وقم بتضاعف البروتوكول بنفسك. المبدئي للإرسال هو تغليف Ethernet DIX القياسي مع البروتوكول المملوء.

مقابس الحزم لا تخضع لسلاسل جدار الحماية للإدخال أو الإخراج.

  التوافقية
في لينكس 2.0، الطريقة الوحيدة للحصول على مقبس حزمة كانت بالاستدعاء:

    socket(AF_INET, SOCK_PACKET, protocol)

هذا لا يزال مدعومًا، لكنه مهمل ومثبط بشدة. الفرق الرئيسي بين الطريقتين هو أن SOCK_PACKET تستخدم struct sockaddr_pkt القديمة لتحديد واجهة، والتي لا توفر استقلالية الطبقة المادية.

    struct sockaddr_pkt {
        unsigned short spkt_family;
        unsigned char  spkt_device[14];
        unsigned short spkt_protocol;
    };

spkt_family يحتوي على نوع الجهاز، spkt_protocol هو نوع بروتوكول IEEE 802.3 كما هو معرف في <sys/if_ether.h> و spkt_device هو اسم الجهاز كسلسلة منتهية بقيمة خالية، على سبيل المثال، eth0.

هذه البنية قديمة ولا ينبغي استخدامها في كود جديد.

العلل

  معالجة رأس LLC
يمكن اعتبار معالجة IEEE 802.2/803.3 LLC كخلل.

  مشكلات MSG_TRUNC
امتداد MSG_TRUNC recvmsg(2) هو اختراق قبيح وينبغي استبداله برسالة تحكم. لا توجد حاليًا طريقة للحصول على عنوان الوجهة الأصلي للحزم عبر SOCK_DGRAM.

  اقتطاع اسم الجهاز spkt_device
حقل spkt_device من sockaddr_pkt بحجم 14 بايت، وهو أقل من الثابت IFNAMSIZ المعرف في <net/if.h> والذي يساوي 16 بايت ويصف حد النظام لاسم واجهة الشبكة. هذا يعني أن أسماء أجهزة الشبكة الأطول من 14 بايت ستُقتطع لتناسب spkt_device. كل هذه الأحجام تتضمن البايت الفارغ الختامي ('\0')).

مشكلات من هذا مع الكود القديم تظهر عادةً مع أسماء واجهات طويلة جدًا تستخدمها ميزة Predictable Network Interface Names المفعلة مبدئيًا في العديد من توزيعات لينكس الحديثة.

الحل المفضل هو إعادة كتابة الكود لتجنب SOCK_PACKET. الحلول الممكنة للمستخدم هي تعطيل Predictable Network Interface Names أو إعادة تسمية الواجهة إلى اسم بطول 13 بايت على الأكثر، على سبيل المثال باستخدام أداة ip(8).

  مشكلات التوثيق
مرشحات المقبس غير موثقة.

انظر أيضًا
socket(2), pcap(3), capabilities(7), ip(7), raw(7), socket(7), ip(8),

RFC 894 لتغليف IP Ethernet القياسي. RFC 1700 لتغليف IP IEEE 802.3.

ملف التضمين <linux/if_ether.h> لبروتوكولات الطبقة المادية.

شجرة مصدر نواة لينكس. يصف Documentation/networking/filter.rst كيفية تطبيق مرشحات حزم بيركلي على مقابس الحزم. يحتوي tools/testing/selftests/net/psock_tpacket.c على كود مصدري أمثلة لجميع الإصدارات المتاحة من PACKET_RX_RING و PACKET_TX_RING.

ترجمة
تُرجمت هذه الصفحة من الدليل بواسطة زايد السعيدي <zayed.alsaidi@gmail.com>

هذه الترجمة هي وثيقة مجانية؛ راجع رخصة جنو العامة الإصدار 3 (https://www.gnu.org/licenses/gpl-3.0.html) أو ما بعده للاطلاع على شروط حقوق النشر. لا توجد أي ضمانات.

إذا وجدت أي أخطاء في ترجمة صفحة الدليل هذه، يرجى إرسال بريد إلكتروني إلى قائمة بريد المترجمين: kde-l10n-ar@kde.org.