|
|
This document is available in: English Castellano Deutsch Francais Nederlands Russian Turkce Arabic |
المؤلف Angel Lopez نبذة عن الكاتب: انجل أنهى دراساته في هندسة الحاسوب. و يعمل أستاذا لدى صن مايكرسيستمز, يعلّم مبادئ سولاريس وإدارة الشبكات . ساعد راما في تأليف كتاب مراسم الإنترنت. التصميم والتطبيق على أنظمة اليُنِكْس. يهتم أساسا بالشبكات و الأمن،و برمجة نظم يُنِكس و شبكاته. و مؤخرا يهتم بتدقيق نواة لينُكس حتى اضطر إلى تقليل نومه. المحتويات: |
نبذة مختصرة:
هذا المقال أريده مقدمة في تقنيات التراسل على شبكات TCP/IP. يعرض المفاهيم النظرية التراسل، و تفاصيل استخدام واجهة برمجة التطبيقات API التي تساعدنا على برمجة تطبيقات التراسل. و سنرى أيضا وظائف النواة التي تدعم هذه التقنية لاستكمال فكرتنا العامة عن دعم التراسل في لينُكْس. و نختم مقالنا بمثال عملي عن برمجة المقابس بلغة C توضيحا لإنشاء التراسل.
حين تريد وصل مضيّف (وصلة) إلى الشبكة،تستطيع أن تستعمل عنوانا من بين ثلاثة أنواع مختلفة من العناوين:
عناوين التراسل مفيدة إذا أردنا إعلام بعض المضيفين دون إذاعة المعلومات في الشبكة. هذا المسعى مثالي حينما يستلزم إرسال معلومات متعددة الوسائط ( كالصوت و الصورة على الفور مثلا) إلى بعض المضيّفين. مما يجعل الاتصال بالمضيفين واحدا بواحد – اتصال وحيد- متعبا وكذلك لا يستحسن إذاعة المعلومات في الشبكة الفرعية – في عنوان إذاعي – لاسيما إن كان أحد المستلمين خارج عن نطاقها
لابد أنك تعرف أن عناوين الإنترنت IP ثلاثة أصناف هي A, B, C و صنف رابع D محجوز للتراسل. العناوين من IPv4 ما بين 224.0.0.0 و 239.255.255.255 تنتمي إلى الصنف D.
البتّات الأربعة العليا من عنوان مرسوم الإنترنت تتراوح ما بين القيم بين 224 و 239. بينما البتّات الثمانية و العشرين الباقية محجوزة لمعرّف مجموعة التراسل ، كما يتضح في الصورة
في مستوى الشبكة، يجب أن يتوافق عنوان Ipv4 مع العنوان الحقيقي للشبكة التي نعمل عليها. إذا كنا نعمل بعنوان شبكة unicast، يمكن معرفة العنوان الحقيقي بالمرسوم ARP والذي لا يجدي نفعا مع عناوين التراسل.لذا نستعمل طرائق أخرى. بعض مراجع RFC تتحدث عن بعض هذه الطرق.
هناك بعض عناوين التراسل الخاصة :
إليك جدولا يعرض كل مجال عناوين التراسل مع الأسماء الشائعة لكل رتبة و عمرها ( العُمر أو زمن الحياة هو عداد الزمن للحياة في حزمة ip TTL's (time to live counter in ip packet) ) . للعمر TTL معنيان. المعنى الأول – ربما يعرفه القارئ – أنه يتحكم في عمر الحزمة فلا تبقى إلى الأبد إذا ما لم تجد طريقها –مما يمنع أي حلقة مفرغة -. و المعنى الثاني أنه يجدد مجال حزمة البيانات حتى لا تغادر الشبكة. مما يتيح تعريف مجالات التراسل اعتمادا على صنف حزم البيانات.
المجال | align=CENTER>TTL | مجال العناوين<</TD> | الوصف |
العقدة | 0 | إنّ حزمة البيانات مقيّدة إلى المضيّف المحلي. و لن يصل أيّا من وصلات الشبكة. | |
الوصلة | 1 | 224.0.0.0 - 224.0.0.255 | حزمة البيانات ستقتصر على المضيف المرسل في الشبكة الفرعية ، ولا تتجاوز أي مسار. |
القسم | < 32 | 239.255.0.0 - 239.255.255.255 | مقتصر على قسم واحد من المنظمة. |
المنظمة | < 64 | 239.192.0.0 - 239.195.255.255 | مقتصر على منظمة معينة. |
عالمي\شامل | < 255 | 224.0.1.0 - 238.255.255.255 | دون قيد، تطبيق عام. |
في شبكة محلية LAN ، واجهة الشبكة لمضيف ما ترسل الرزم التي تقصد عنوان المضيف بعينه إلى الطبقات العليا .هذه الرزم إما تحمل عنوان الحقيقي للمضيف أو هي رزم مذاعة. إذا انضم المضيّف إلى زمرة تراسلٍ ، فإن واجهة الشبكة ستعترف أيضا بالرزم التي تقصد الزمرة أي كل رزمة تقصد عنوان زمرة التراسل التي ينتمي إليها المضيف. لذا إذا كان عنوان المضيف الطبيعي 80:C0:F6:A0:4A:B1 انضم إلى زمرة تراسلٍ عنوانها 224.0.1.10 فسيتعرف على الرزم التي عليها أحد هذه العناوين :
المسارات ترسل أيضا رسائل IGMP إلى الزمرة 224.0.0.1 تطلب كلّ المعلومات اللازمة عن الزمرة التي انضم إليها المضيف. و حينما يستلم المضيف هذه الرسالة يضع في عداد متناقص قيمة ما –عشوائيا – و سيجيب عندما تنعدم. حتى لا يجيب كلّ المضيّفين في الوقت نفسه ولا تتحمل الشبكة عبئا زائدا . وحين يجيب المضيف يرسل رسالة إلى عنوان الزمرة فيتلقاها كل المضيفين الأعضاء. فلا يجيبون بأنفسهم إذ يكفي مضيف واحد حتى يتعامل مسار الشبكة الفرعية مع الرسائل المتراسلة لهذه للزمرة
إذا انسحب كلّ المضيّفين من الزمرة فلا أحد سيجيب، عندئذ يقرر المسار أن لا أحد يهتم بهذه الزمرة فيتركها و لا يوجه إليها أي رسالة. خيار آخر مدعوم في IGMPv2 هو أن يرسل المضيف رسالة انسحاب من الزمرة إلى العنوان 224.0.0.2.
حسب الخبرة السابقة في برمجة المقابس، سيجد القارئ خمسا من عمليات المقابس الجديدة فقط ليتعامل مع خيارات التراسل . الدالة setsockopt () ستستعمل لتعديل هذه الخيارات و getsockopt () لقراءتها. و الجدول يعرض خيارات التراسل و أنواع بياناتها المستعملة و يوجز وصفها:
خيارات IPv4 | نوع البيانات | الوصف |
IP_ADD_MEMBERSHIP | struct ip_mreq | إنضمّ إلى زمرة التراسل. |
IP_DROP_MEMBERSHIP | struct ip_mreq | انسحب من زمرة التراسل. |
IP_MULTICAST_IF | struct ip_mreq | حدّد وصلة لاستلام الرسائل المتراسلة. |
IP_MULTICAST_TTL | u_char | حدّد TTL عمرا لاستلام الرسائل المتراسلة. |
IP_MULTICAST_LOOP | u_char | عطّل رجوع الرسائل المتراسلة أو نشطها loopback |
البنية ip_mreq معرفة في ملف التصدير <linux/in.h> هكذا:
struct ip_mreq { struct in_addr imr_multiaddr; /* IP multicast address of group */ struct in_addr imr_interface; /* local IP address of interface */ };
#define IP_MULTICAST_IF 32 #define IP_MULTICAST_TTL 33 #define IP_MULTICAST_LOOP 34 #define IP_ADD_MEMBERSHIP 35 #define IP_DROP_MEMBERSHIP 36
لينضم مسعى process إلى زمرة يرسل بمقبس بالدالة setsockopt(). و وسيطها بنية ip_mreq: حيث حقلها الأول imr_multiaddr فيه عنوان الزمرة المقصودة ، و الثاني imr_interface فيه عنوان واجهة المضيف.
استعمل هذا الخيار للانسحاب من الزمرة. حقول البنية ip_mreq مستعملة كما في الانضمام.
استعمل هذا الخيار لتحديد واجهة الشبكة التي يستعملها المقبس للتراسل. حقول البنية ip_mreq مستعملة كما في الانضمام.
حدد به عمر TTL Time To Live رزم التراسلالمرسلة بالمقبس. القيمة تلقائيا 1 أي أن رزمة البيانات لن تتجاوز الشبكة الفرعية.
حين يرسل مسعى process رسالة متعددة إلى الزمرة ، سيستلم نفس الرسالة إن كان منضما إليها و إلا رجعت إليه رسالته. هذا الخيار يعطل هذا التصرف أو يفعِّله.
هذا مثال بسيط لاختبار ما تعرضنا إليه من أفكار . المسعى يراسل الزمرة ، و المساعي الأخرى في المجموعة تشاهد الرسائل ثم تظهرها على الشاشة. هذا المتن يصنع خادما يرسل إلى الزمرة 224.0.1.1 كل ما يدخله من مداخله القياسية. و لا حاجة لأي فعل خاص للتراسل بل يكفي عنوان الزمرة. يمكنك أن تعدل خيارات الارتجاع و العمر إذا لم توافق القيم التلقائية التطبيق المزمع.
المدخلات القياسية ترسل إلى عنوان زمرة تراسلٍ 224.0.1.1
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <stdio.h> #define MAXBUF 256 #define PUERTO 5000 #define GRUPO "224.0.1.1" int main(void) { int s; struct sockaddr_in srv; char buf[MAXBUF]; bzero(&srv, sizeof(srv)); srv.sin_family = AF_INET; srv.sin_port = htons(PUERTO); if (inet_aton(GRUPO, &srv.sin_addr) < 0) { perror("inet_aton"); return 1; } if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); return 1; } while (fgets(buf, MAXBUF, stdin)) { if (sendto(s, buf, strlen(buf), 0, (struct sockaddr *)&srv, sizeof(srv)) < 0) { perror("recvfrom"); } else { fprintf(stdout, "Enviado a %s: %s\n", GRUPO, buf); } } }
إليك المتن الذي يبين جانب الزبون. الزبون يتلقى الرسائل التي يبعثها الخادم إلى الزمرة. الرسائل المستلمة تظهر على المخرج القياسي. يتميز هذا المتن بالخيار IP_ADD_MEMBERSHIP وما عدا ذلك عادي لاتصال المسعى حسب مرسوم UDP .
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #define MAXBUF 256 #define PUERTO 5000 #define GRUPO "224.0.1.1" int main(void) { int s, n, r; struct sockaddr_in srv, cli; struct ip_mreq mreq; char buf[MAXBUF]; bzero(&srv, sizeof(srv)); srv.sin_family = AF_INET; srv.sin_port = htons(PUERTO); if (inet_aton(GRUPO, &srv.sin_addr) < 0) { perror("inet_aton"); return 1; } if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); return 1; } if (bind(s, (struct sockaddr *)&srv, sizeof(srv)) < 0) { perror("bind"); return 1; } if (inet_aton(GRUPO, &mreq.imr_multiaddr) < 0) { perror("inet_aton"); return 1; } mreq.imr_interface.s_addr = htonl(INADDR_ANY); if (setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq)) < 0) { perror("setsockopt"); return 1; } n = sizeof(cli); while (1) { if ((r = recvfrom(s, buf, MAXBUF, 0, (struct sockaddr *) &cli, &n)) < 0) { perror("recvfrom"); } else { buf[r] = 0; fprintf(stdout, "Mensaje desde %s: %s\n", inet_ntoa(cli.sin_addr), buf); } } }
كما vHdkh رأينا آنفا ،فإن المسعى إذا أراد الانضمام إلى زمرة تراسلٍ يستعمل الدالة setsockopt () لتعديل خيار IP_ADD_MEMBERSHIP في مستوى IP . متن هذه الدالة تجده في /usr/src/linux/net/ipv4/ip_sockglue.c . و إليك متن الدالة الذي تعدل به هذا الخيار و خيار IP_DROP_MEMBERSHIP .
struct ip_mreqn mreq; if (optlen < sizeof(struct ip_mreq)) return -EINVAL; if (optlen >= sizeof(struct ip_mreqn)) { if(copy_from_user(&mreq,optval,sizeof(mreq))) return -EFAULT; } else { memset(&mreq, 0, sizeof(mreq)); if (copy_from_user(&mreq,optval,sizeof(struct ip_mreq))) return -EFAULT; } if (optname == IP_ADD_MEMBERSHIP) return ip_mc_join_group(sk,&mreq); else return ip_mc_leave_group(sk,&mreq);
الأسطر الأولى تفحص الوسيط المدخل ip_mreq struct إن كان طوله صحيحا. ويمكن نسخه من المستعمل إلى النواة. عندما نحصل على قيمة الوسيط، ننفذ الدالة ip_mc_join_group() للانضمام أو ip_mc_leave_group() للانسحاب. متن هاتين الدالتين موجود في /usr/src/linux/net/ipv4/igmp.c . و هاهو ذا متن الانضمام:
int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr) { int err; u32 addr = imr->imr_multiaddr.s_addr; struct ip_mc_socklist, *iml, *i; struct in_device *in_dev; int count = 0;
بادئ ذي بدء نتحقق – بالمختصر MULTICAST - أن عنوان الزمرة في مجال العناوين المحجوزة للتراسل. لذلك يكفي أن نتحقق أن البايت- الثمنة- الأعلى يساوي 224.
if (!MULTICAST(addr)) return -EINVAL; rtnl_shlock();
بعد التحقّق، تبدأ واجهة الشبكة التعامل مع الزمرة . إذا تعذر الدخول بواسطة الدليل إلى الواجهة - كما يجب حسب IPv6- تنفذ الدالة لإيجاد الملحق device المرتبط بعنوان IP المحدد. سنفترض فيما تبقى من المقال أنها كذلك مادمنا نعمل على Ipv4 . إذا كان العنوان INADDR_ANY ، على النواة أن تجد بنفسها واجهة الشبكة ، إذ تقرأ جدول المسالك و تختار الوصلة الفضلى آخذة في الحسبان عنوان الزمرة و تعريف جدول المسالك.
if (!imr->imr_ifindex) in_dev = ip_mc_find_dev(imr); else in_dev = inetdev_by_index(imr->imr_ifindex); if (!in_dev) { iml = NULL; err = -ENODEV; goto done; }
ثمّ نحجز الذاكرة للبنية ip_mc_socklist، و نقارن أي عنوان الزمرة و واجهة متصل بالمقبس. فإذا ارتبط المقبس بعنوان ما سابقا، نترك الدالة إذ لا يكمن أن تربط مرتين زمرة و واجهة. إذا لم تكن عناوين واجهة الشبكة من نوع INADDR_ANY ،يزداد العداد المطابق قبل أن تنتهي الدالة.
iml = (struct ip_mc_socklist *)sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL); err = -EADDRINUSE; for (i=sk->ip_mc_list; i; i=i->next) { if (memcmp(&i->multi, imr, sizeof(*imr)) == 0) { /* New style additions are reference counted */ if (imr->imr_address.s_addr == 0) { i->count++; err = 0; } goto done; } count++; } err = -ENOBUFS; if (iml == NULL || count >= sysctl_igmp_max_memberships) goto done;
إذا وصلنا إلى هذه النقطة فذها يعني بأنّ مقبسا جديد سيرتبط بزمرة لذا يجب إنشاء مدخل جديد و ربطه بقائمة الزمر التي تنتمي إلى المقبس. الذاكرة محجوزة مقدما، لذا يكفي أن نضع القيم الصحيحة في الحقول المناسبة للبنية.
memcpy(&iml->multi,imr, sizeof(*imr)); iml->next = sk->ip_mc_list; iml->count = 1; sk->ip_mc_list = iml; ip_mc_inc_group(in_dev,addr); iml = NULL; err = 0; done: rtnl_shunlock(); if (iml) sock_kfree_s(sk, iml, sizeof(*iml)); return err; }
الدالة ip_mc_leave_group() تستعمل للانسحاب من زمرة التراسُل، و هي أسهل من الدالة السابقة. إذ تبحث عن عنوان الزمرة و عنوان الواجهة من بين المداخل المرتبطة بالمقبس وحين تجدهما يتناقص عدد الإشارات لأن مسعى انسحب من الزمرة. وإذا انعدمت القيمة الجديدة يُحذف العداد.
int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) { struct ip_mc_socklist *iml, **imlp; for (imlp=&sk->ip_mc_list;(iml=*imlp)!=NULL; imlp=&iml->next) { if (iml->multi.imr_multiaddr.s_addr==imr->imr_multiaddr.s_addr && iml->multi.imr_address.s_addr==imr->imr_address.s_addr && (!imr->imr_ifindex || iml->multi.imr_ifindex==imr->imr_ifindex)) { struct in_device *in_dev; if (--iml->count) return 0; *imlp = iml->next; synchronize_bh(); in_dev = inetdev_by_index(iml->multi.imr_ifindex); if (in_dev) ip_mc_dec_group(in_dev, imr->imr_multiaddr.s_addr); sock_kfree_s(sk, iml, sizeof(*iml)); return 0; } } return -EADDRNOTAVAIL; }
الخيارات التراسل الأخرى التي تطرقنا إليها بسيطة. لأنها تُعدِّل قيم حقول البنية الداخلية للمقبس المستعمل. هذا ما تؤديه الدالة ip_setsockopt().
source code | المتن |
Multicast | التراسل |
Broadcast | البث |
Routing table | جدول المسالك |
Function | الدالة |
Group | زمرة |
multicast | تراسل القوم تراسلا : أي راسل بعضهم بعضا |
broadcast | بث الخبر باث أذاعه. |
|
الصفحات تحت رعاية الفريق الفني للينكس فوكاس
© Angel Lopez, FDL LinuxFocus.org اضغط هنا للتنبيه عن خطأ أو ارسال ملاحظاتك الى لينكس فوكاس |
معلومات عن الترجمة:
|
2001-05-21, generated by lfparser version 2.13