[LinuxFocus-icon]
Home  |  Map  |  Index  |  Search

News | Archives | Links | About LF  
This article is available in: English  Castellano  Deutsch  Francais  Nederlands  Russian  Turkce  Arabic  
[Photo of the Author]
المؤلف

نبذة عن الكاتب:

انجل أنهى دراساته في هندسة الحاسوب. و يعمل أستاذا لدى صن مايكرسيستمز, يعلّم مبادئ سولاريس وإدارة الشبكات . ساعد راما في تأليف كتاب مراسم الإنترنت. التصميم والتطبيق على أنظمة اليُنِكْس. يهتم أساسا بالشبكات و الأمن،و برمجة نظم يُنِكس و شبكاته. و مؤخرا يهتم بتدقيق نواة لينُكس حتى اضطر إلى تقليل نومه.


المحتويات:

 

التراسل MultiCast

[Ilustration]

نبذة مختصرة:

هذا المقال أريده مقدمة في تقنيات التراسل على شبكات TCP/IP. يعرض المفاهيم النظرية التراسل، و تفاصيل استخدام واجهة برمجة التطبيقات API التي تساعدنا على برمجة تطبيقات التراسل. و سنرى أيضا وظائف النواة التي تدعم هذه التقنية لاستكمال فكرتنا العامة عن دعم التراسل في لينُكْس. و نختم مقالنا بمثال عملي عن برمجة المقابس بلغة C توضيحا لإنشاء التراسل.



 

مقدمة

حين تريد وصل مضيّف (وصلة) إلى الشبكة،تستطيع أن تستعمل عنوانا من بين ثلاثة أنواع مختلفة من العناوين:



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

 

عنوان التراسل

لابد أنك تعرف أن عناوين الإنترنت IP ثلاثة أصناف هي A, B, C و صنف رابع D محجوز للتراسل. العناوين من IPv4 ما بين 224.0.0.0 و 239.255.255.255 تنتمي إلى الصنف D.

البتّات الأربعة العليا من عنوان مرسوم الإنترنت تتراوح ما بين القيم بين 224 و 239. بينما البتّات الثمانية و العشرين الباقية محجوزة لمعرّف مجموعة التراسل ، كما يتضح في الصورة

[grouped bits]


في مستوى الشبكة، يجب أن يتوافق عنوان Ipv4 مع العنوان الحقيقي للشبكة التي نعمل عليها. إذا كنا نعمل بعنوان شبكة unicast، يمكن معرفة العنوان الحقيقي بالمرسوم ARP والذي لا يجدي نفعا مع عناوين التراسل.لذا نستعمل طرائق أخرى. بعض مراجع RFC تتحدث عن بعض هذه الطرق.

وترجمته



في شبكات Ethernet الأكثر اتساعا منها ، للمطابقة نضع في البتّات ال24 الأعلى القيمة 01:00:5E . و البت الموالي معدوم أي يساوي الصفر، والبتّات ال23 الأدنى تستعمل البتات ال23 الأدنى من عنوان تراسل كما يظهر في الرسم :
[transform to Ethernet]

مثلا عنوان Ipv4 للتراسل 224.0.0.5 سيطابق العنوان الحقيقي للإيثرنت 01:00:5E:00:00:05 .

هناك بعض عناوين التراسل الخاصة :

كثير من عناوين التراسل محجوزة عدا المذكورة آنفا . يمكنك أن تطّلع على معلومات وافية في آخر نسخة من " الأرقام المحجوزة" مراجع 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 فسيتعرف على الرزم التي عليها أحد هذه العناوين :

للتراسل على شبكة واسعة WAN يجب أن تدعم المسارات التراسل. عندما ينضم "مسعى" process إلى زمرة ما يرسل المضيف رسالة IGMP (Internet Group Management Protocol) ، مرسوم إدارة زمر الانترنت إلى كل مسار تراسلي في الشبكة الفرعية، لإعلامهم أن يوجهوا الرسائل تراسل إلى الشبكة الفرعية ليتمكن المسعى من استقبالها. و المسارات ستخبر المسارات الأخرى أن توجه إليها أيّ رسائل متراسلة خاصة بالشبكة الفرعية.

المسارات ترسل أيضا رسائل IGMP إلى الزمرة 224.0.0.1 تطلب كلّ المعلومات اللازمة عن الزمرة التي انضم إليها المضيف. و حينما يستلم المضيف هذه الرسالة يضع في عداد متناقص قيمة ما –عشوائيا – و سيجيب عندما تنعدم. حتى لا يجيب كلّ المضيّفين في الوقت نفسه ولا تتحمل الشبكة عبئا زائدا . وحين يجيب المضيف يرسل رسالة إلى عنوان الزمرة فيتلقاها كل المضيفين الأعضاء. فلا يجيبون بأنفسهم إذ يكفي مضيف واحد حتى يتعامل مسار الشبكة الفرعية مع الرسائل المتراسلة لهذه للزمرة

إذا انسحب كلّ المضيّفين من الزمرة فلا أحد سيجيب، عندئذ يقرر المسار أن لا أحد يهتم بهذه الزمرة فيتركها و لا يوجه إليها أي رسالة. خيار آخر مدعوم في IGMPv2 هو أن يرسل المضيف رسالة انسحاب من الزمرة إلى العنوان 224.0.0.2.

 

واجهة برمجة التطبيقات API

حسب الخبرة السابقة في برمجة المقابس، سيجد القارئ خمسا من عمليات المقابس الجديدة فقط ليتعامل مع خيارات التراسل . الدالة 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


 

الانضمام IP_ADD_MEMBERSHIP

لينضم مسعى process إلى زمرة يرسل بمقبس بالدالة setsockopt(). و وسيطها بنية ip_mreq: حيث حقلها الأول imr_multiaddr فيه عنوان الزمرة المقصودة ، و الثاني imr_interface فيه عنوان واجهة المضيف.

 

الانسحاب IP_DROP_MEMBERSHIP

استعمل هذا الخيار للانسحاب من الزمرة. حقول البنية ip_mreq مستعملة كما في الانضمام.

 

تحديد الاستلام IP_MULTICAST_IF

استعمل هذا الخيار لتحديد واجهة الشبكة التي يستعملها المقبس للتراسل. حقول البنية ip_mreq مستعملة كما في الانضمام.

 

تحديد العمر IP_MULTICAST_TTL

حدد به عمر TTL Time To Live رزم التراسلالمرسلة بالمقبس. القيمة تلقائيا 1 أي أن رزمة البيانات لن تتجاوز الشبكة الفرعية.

 

الارتجاع IP_MULTICAST_LOOP

حين يرسل مسعى 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

اضغط هنا للتنبيه عن خطأ أو ارسال ملاحظاتك الى لينكس فوكاس
معلومات عن الترجمة:
es -> --
es -> en
en -> ar

2001-05-21, generated by lfparser version 2.13