Probleme mit UDP Broadcast

From: <emfau(at)t-online.de>
Date: Fri, 18 Nov 2005 12:44:31 +0100

Hallo,

ich habe versucht die Version 2.0.3 von heartbeat (aka linux-ha) 'manuell' zu
installieren, da im Portstree bisher leider nur 1.2.3 vorhanden ist.
Nach dem Probleme mit Konfiguration und Uebersetzung geloest sind, wollte ich
einen simplen Cluster testen, der den failover einer IP-Adresse erlaubt. Die
'Abstimmung' bzw. die Kontrolle der Knoten untereinander wird hierbei mittels
eines Heartbeat-Protokolls geregelt, wobei als Medium fuer den Austausch von
Nachrichten unter anderem UDP Broadcast konfigurierbar ist.

Wenn die Broadcast-Nachrichten allerdings eine Groesse von 1472 Bytes
ueberschreiten, gibt es den Fehler "Message too long". Die Interfacs, ueber die
die Nachrichten laufen, hat eine MTU von 1500, was nach meinem Verstaendnis
bedeutet, dass es genau der Punkt ist, ab dem das Paket fragmentiert werden
muesste, was aber nicht passiert - anstatt gibt es eben besagte Fehlermeldung.

Als Versuchsaufbau habe ich das Ganze mit zwei VMware-Instanzen versucht, in
denen entweder FreeBSD 4.11 Stable oder 5.4 Stable laeuft. Da hierbei die
Kommunikation ueber einen von VMWare simulierten Hub laeuft, der ein Problem
darstellen koennte, habe ich auch zwei 'reale' Hardware-Boxen + Hub probiert -
gleiches Verhalten. Nehme ich als Sender aber ein Knoppix, geht es wunderbar...
Ich kann Packete beliebiger Groesse verschicken, die von FreeBSD auch korrekt
emfpangen werden. Schneidet man den Verkehr per tcpdump mit, kann man sehen,
das die Pakete entsprechend fragmentiert werden.

Die Heartbeat-Entwickler haben mir zur Isolation des Problemes ein Code-
Schnippsel geschickt, in dem nur diese Broadcast-Kommunikation getestet wird.
Ich haenge es fuer mal Interessiert an.

Ich habe die man pages von socket, setsockopt, sendto et al durchforstet, aber
nichts zu diesem Thema gefunden, auch google und verschiedene RFCs waren nicht
sehr hilfreich.
Deswegen meine Frage an die Weisen der Liste.
Ist das beobachtete Verhalten 'normal', d.h. sind UDP Broadcasts unter FreeBSD
schlicht in der Groesse beschraenkt?
Kann man das Verhalten irgendwie beeinflussen (sysctl o.ae.)?
Anderweitige Kommentare oder Hinweise?

Danke,
  Michael

$ gcc -g -Wall -o bctest bcast.c

<bcast.c>

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <net/if.h>
#include <errno.h>
#include <sys/ioctl.h>

#define PORT 694
#define MAXBUF 2048

void
usage(int argc, char ** argv)
{
     printf("Usage: %s [-s] [-r] -p packetsize -i eth0/eth1/...\n", argv[0]);
     printf("#default is to receive\n");
     return;
}

int
create_recv_socket(char* ifname)
{
     struct sockaddr_in my_addr;
     int sockfd;
     int one = 1;
     struct ifreq i;

     memset(&my_addr, 0, sizeof(my_addr));
     my_addr.sin_family = AF_INET;
     my_addr.sin_port = htons(PORT);
     my_addr.sin_addr.s_addr = htonl(INADDR_ANY);

     if (( sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
         printf("Error getting socket: %s\n", strerror(errno));
         exit(1);
     }

     if (setsockopt( sockfd, SOL_SOCKET,
             SO_REUSEADDR, (const void*)&one, sizeof(one)) < 0){
         printf("Error setting socket option SO_REUSEADDR: %s\n",
                strerror(errno));
         exit(1);
     }

     strcpy(i.ifr_name, ifname);

     if (bind(sockfd, (struct sockaddr *) & my_addr, sizeof(struct sockaddr))
         < 0){
         printf("Error binding socket: %s\n", strerror(errno));
         exit(1);
     }

     return sockfd;
}

void
recv_packets(int sockfd, int packetsize)
{
     int bufsize = packetsize + 100;
     char buf[MAXBUF];
     int n;
     struct sockaddr_in theiraddr;
     int len = sizeof(theiraddr);

     while(1) {

         memset(buf, 0, MAXBUF);
         if ((n = recvfrom(sockfd, buf, bufsize - 1, 0,
                   (struct sockaddr*)&theiraddr, &len)) == -1){
             printf("Error receiving from socket: %s\n", strerror(errno));
             exit(1);
         }
         printf("Message received: (%d bytes)\n%s\n", n, buf);
     }

}

int
create_send_socket(char* ifname)
{
     int sockfd;
     int one = 1;
     struct ifreq i;
     strcpy(i.ifr_name,ifname);

     if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) <= 0) {
         printf("Error in getting socket: %s", strerror(errno));
         exit(1);
     }

     if (setsockopt(sockfd, SOL_SOCKET,
             SO_BROADCAST, (const void*) &one, sizeof(one)) == -1) {
         printf("Error setting socket option SO_BROADCAST: sockfd =%d , %s\n",
                    sockfd, strerror(errno));
         close(sockfd);
         exit(1);
     }

     return sockfd;
}

int
get_baddr(struct sockaddr_in* baddr, char* ifname)
{
     struct ifreq ifr;
     int fd;
     int return_val;

     fd = socket (PF_INET, SOCK_DGRAM, 0);

     if (fd <0) {
         printf("Error opening socket: %s\n", strerror(errno));
         exit(1);
     }

     strcpy (ifr.ifr_name, ifname);

     return_val = ioctl(fd, SIOCGIFBRDADDR, &ifr);
     if (return_val == 0 ) {

         if (ifr.ifr_broadaddr.sa_family == AF_INET) {
             struct sockaddr_in sin_ptr;
             memcpy(&sin_ptr, &ifr.ifr_broadaddr,
                         sizeof(sin_ptr));
             memcpy(&baddr->sin_addr, &sin_ptr.sin_addr,
                         sizeof(baddr->sin_addr));
         } else {
             printf("Wrong family for broadcast interface : %s",
                     strerror(errno));
             exit(1);
         }
     } else {
         printf("Get broadcast for interfade failed: %s\n", strerror(errno));
         exit(1);
     }

     baddr->sin_family = AF_INET;
     baddr->sin_port = htons(PORT);

     return 0;
}

void
send_packets(int sockfd, struct sockaddr* addr, int packetsize)
{
     int i;
     char packet[MAXBUF];
     char size[10];
     int ret;

     memset(packet, 'A', packetsize);
     packet[packetsize-1] = '\0';
     sprintf(size, "%d", packetsize);
     strcpy(packet, size);
     packet[strlen(size)] = ' ';

     for ( i = 0; i < 1000; i++) {
         printf("sending packet %d\n", i);
         if ((ret = sendto(sockfd, packet,
                 packetsize, 0,addr ,sizeof(struct sockaddr))) != packetsize) {
             printf("Unable to send bcast packets: %s\n", strerror(errno));
             exit(1);
         }
     }

}

int
main(int argc, char** argv)
{

     int issender = 0;
     int isrecver = 0;
     int packetsize = 0;
     int c;
     char* ifname;

     while ((c = getopt (argc, argv, "sri:p:")) != -1)
         switch(c) {
             case 's':
                 issender =1;
                 break;
             case 'r':
                 isrecver =1;
                 break;
             case 'i':
                 ifname = optarg;
                 break;
             case 'p':
                 packetsize = atoi(optarg);
                 break;
             case '?':
                 printf("unknown options\n");
                 usage(argc, argv);
                 return(1);
             }

     if (issender && isrecver) {
         printf("sender and recver in one program"
                 "is not supported\n");
         usage(argc, argv);
         exit(1);
     }

     if (packetsize == 0) {
         printf("invalid packetsize\n");
         usage(argc, argv);
         exit(1);
     }

     if (issender) {
         int send_sockfd = create_send_socket(ifname);
         struct sockaddr_in baddr;

         get_baddr(&baddr, ifname);
         send_packets(send_sockfd,(struct sockaddr*)&baddr, packetsize);
         printf("Sending finished!\n");
     } else {
         int recv_sockfd = create_recv_socket(ifname);
         recv_packets( recv_sockfd, packetsize);
     }

     printf("Program ends successfully!\n");
     return 1;
}

</bcast.c>

To Unsubscribe: send mail to majordomo(at)de.FreeBSD.org
with "unsubscribe de-bsd-questions" in the body of the message
Received on Fri 18 Nov 2005 - 12:45:42 CET

search this site