nem zsiványság: raw socket, melyik interfész, broadcast

 ( tovis | 2009. szeptember 18., péntek - 17:11 )

Hálózat szimulációhoz írtam (valahol 2006 -ban) egy kis raw socket progit, ami napló alapján küldözget, apró csomagocskákat mintha azok különféle I címről jönnének. Most leporoltam ezt a progit, mivel a változatosság kedvéért broadcast csomagokat kellene küldenem a 255.255.255.255 -ös címre, ismét különféle ip címek szimulációjával. Hát nem megy. A 255.255.255.255 címre nem tudok küldeni "Permission denied" hibaüzenetet dob a program (természetesen root -ként futtatom). Ráadásul a gépben két hálókártya is van, viszont a socket nincs "bind" -elve egyikre sem.
Kérdések:
- miért nem működik a raw küldés a 255.255.255.255 címre?
- hogy kell egy raw socketet rábírni hogy egy adott interfészre dolgozzon?

Eddig nem találtam hasznosat a neten :(

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

Ez a fuggveny egy Wake-On-LAN csomag kuldo programombol van, osszeallitja a WOL csomagot es kikuldi minden egyes interfeszen, ez alapjan elindulhatsz.

void SendWOLPacket(struct ether_addr * MACBin) {
  int i;
  sockfd=socket(PF_PACKET,SOCK_RAW,0);
  if (sockfd==-1)
    return;
  iflist=if_namelist();
  if (iflist==NULL) {
    close(sockfd); sockfd=-1;
    return;
  }
  for (i=0; iflist[i]!=NULL; i++) {
    int Status;
    int ifindex;
    struct ifreq ifr;
    byte PacketBuffer[256];
    dword PacketLength;
    byte * ptr;
    int cnt;
    struct sockaddr_ll dest;
    strcpy(ifr.ifr_name,iflist[i]);
    Status=ioctl(sockfd,SIOCGIFFLAGS,&ifr);
    if (Status==-1)
      continue;
    if ((ifr.ifr_flags & IFF_UP)==0 || (ifr.ifr_flags & IFF_LOOPBACK)!=0 || (ifr.ifr_flags & IFF_MASTER)!=0)
      continue;
    strcpy(ifr.ifr_name,iflist[i]);
    Status=ioctl(sockfd,SIOCGIFHWADDR,&ifr);
    if (Status==-1 || (Status!=-1 && ifr.ifr_hwaddr.sa_family!=ARPHRD_ETHER))
      continue;
    ptr=PacketBuffer;
    memcpy(ptr,&MACBin->ether_addr_octet,ETH_ALEN);
    ptr+=ETH_ALEN;
    memcpy(ptr,&ifr.ifr_hwaddr.sa_data,ETH_ALEN);
    ptr+=ETH_ALEN;
    *(word *)ptr=htons(WOL_ETHER_TYPE);
    ptr+=sizeof(word);
    strcpy(ifr.ifr_name,iflist[i]);
    Status=ioctl(sockfd,SIOCGIFINDEX,&ifr);
    if (Status==-1)
      continue;
    ifindex=ifr.ifr_ifindex;
    memset(ptr,0xFF,ETH_ALEN);
    ptr+=ETH_ALEN;
    for (cnt=1; cnt<=16; cnt++) {
      memcpy(ptr,&MACBin->ether_addr_octet,ETH_ALEN);
      ptr+=ETH_ALEN;
    }
    PacketLength=(dword)ptr-(dword)PacketBuffer;
    dest.sll_family=AF_PACKET;
    dest.sll_ifindex=ifindex;
    dest.sll_halen=ETH_ALEN;
    memcpy(&dest.sll_addr,MACBin->ether_addr_octet,sizeof(MACBin->ether_addr_octet));
    sendto(sockfd,PacketBuffer,PacketLength,0,(struct sockaddr *)&dest,sizeof(dest));
  }
  if_freenamelist(iflist); iflist=NULL;
  close(sockfd); sockfd=-1;
}

(az if_namelist() sajat fuggveny, a /proc/net/dev-bol olvassa ki az interfeszek listajat, a byte/word/dword tipusok az uint8/16/32-t jelentik)

- miért nem működik a raw küldés a 255.255.255.255 címre?
Lehet, hogy azért amiért a ping 255.255.255.255 sem?

Te magad allitod elo az IP csomagot? A kernel elvben nem foglalkozik vele, hogy mit irsz bele, hanem ugy ahogy van, kiteszi a drotra.

sendip-vel el tudod kuldeni a 255.255.255.255-re?

Csak tippek, jó pongyolán:

Mivel nem multicast-ról hanem broadcast-ról beszélsz, azért vélhetően 1 db IP subnet-re / interface-re / ethernet szegmensre vagy korlátozva. A csomagodra ránézve a kernel több illeszkedő routing bejegyzést is talál, és valószínűleg 1 db sendmsg() hatására nem hajlandó két interface-en is beszélni. (Ha belegondolsz, miért álljunk meg két interface-nél; a 255.255.255.255-re küldve akár még a loopback-en is értelmesnek tekinthetnénk a broadcast megjelenését.)

Szerintem próbáld meg az egyes subnet-ek saját broadcast címét használni, és minden interface-en külön küldd ki a csomagot, ugyanannak a bind-olatlan socket-nek a felhasználásával. Vagy az elején csinálj két socket-et, külön-külön bind-olva (nem tudom, ez értelmes javaslat-e raw socket-nél: elvileg még protokollt sem választottál, így hogyan választhatnál portot). Vagy bridge-eld össze a két kártyát egyetlen ethernet szegmensbe (ha ezt így mondják), és akkor 1 db közös broadcast cím fog hozzájuk tartozni (gondolom).

hali, ezen a linken el tudsz indulni:
http://www.security-freak.net/packet-injection/packet-injection.html

vagy ha az általad írt forrást megmutatod, lehet, hogy megtaláljuk a hibát.
üdv,
sbalazs.

Először is mindenkinek köszönöm a hozzászólást!
Mivel órákig nem jött semmi (anno) feladtam, és a "broadcast" csomagok helyett sima UDP csomagokat küldözgettem, különféle "fake" forráscímmel ellátva - a teszthez ez elégséges volt. Úgyhogy megkerültem a problémát :)
Meglepő, hogy a raw socket és az egyéb, nem szigorúan felhasználói kommunikációval kapcsolatban (pl. ICMP) milyen kevés az információ/specifikáció. Ráadásul, mivel mind winben mind Linuxban programozgatok - tudathasadásom is van, mivel a két rwendszer számos ponton alapvető dolgokat másképp kezel. Továbbra sem tudom, hogy a RAW socket melyik ethernet kártyára kapcsolódik - úgy tűnik, hogy az IP címhez illeszt valahogy (lehet hogy a másik szegmensre definiált interfész is megkapja, csak kiszűri?)

Egébként, most épp a windows ping -et "kerülgettem", számos alig olvasható forráskódot átnéztem, mindegyik az icmp.dll API -ra támaszkodik amit az M$ eléggé elkenve dokumentált, azt is csak a CE verzióra (MSDN, SDK és egyéb WEB források). Néha azon csodálkozom, hogy tud ez az egész működni amikor ilyen barkács!?

* Én egy indián vagyok. Minden indián hazudik.

Még mindig ugyanaz a projekt, de most kicsit más a gond.
Van egy kis raw socket küldözgető progim - kopasz C, az egész alig több 10 KB forrásnál. x.x.0.0 -tól x.x.3.254 -ig szór álvéletlen címekkel csomagokat. A program "magját" a csomag összeállítás képezi, teljesen szinkronban nyomja a csomagokat ahogy bírja. "Normál" (én annal tekintem állapotában ck. 2700 csomagot dob másodpercenként, a megadott cím és socket irányába. A túloldalon van a fejlesztés alatt álló program, ami veszi ezeket a csomagokat, és lerakja érkezési sorrendben egy fájlba, és ez windows -on zajlik. Azt kellet észrevennem, hogy időnként - teljesen változóan, akár 10 millió csomag után - egyszer csak kimerevedik, a log -ban azt látni, hogy az alsó réteg, eldobálja a csomagokat és a log FIFO teljesen betelik. Órákon át próbáltam mgérteni miért. össze-vissza trancsíroztam a kódot, míg rájöttem, hogy a primitív Linux oldali teszt programom időnként megvadul!? A szokásos 2.700 csomag helyett több mint 10.000 csomagot kezd küldeni! A Linux oldalt nem igen piszkálom, semmire sem használom, mitől tud ez így megtáltosodni? Hogy tudnám, valami egyszerű de biztonságos módon megfogni a sebességet?
(Kicsit lepusztult az agyam a sok órányi fölösleges debuggolástól)

* Én egy indián vagyok. Minden indián hazudik.

A halozati eszkozok (hw-k) egeszen turhetoen kezelik a burst-okben kiadott csomagokat. Ezen felul, idoziteni, pl masodpercenkent _egyenletesen_ kikuldeni mondjuk 1000 csomagot elegge nehez (me'g tobbet meg nehezebb). Osszekapcsolva a kettot: masodpercenkent pl. 50x kikuldeni 20-as adagokban a csomagokat mar sokkal jobban kivitelezheto (egyenletesebb, stabilabb). Ebbe me'g beleteszel egy kis abszolut ido merest, novekmenyes algoritmussal, nanosleep/usleep/select + gettimeofday alapokon, es remelhetoleg jo lesz.

A.

"Egy kis önreklám sosem árt" mottóval ajánlom gyors belekukkantásra a timer_design.txt-t és a timer.[ch]-t az udp_copy2-ből, vagy a debug üzeneteket az atomfapad udpconn2.c-ből.

Mi lehet a hiba?

struture timespec upto, remain;
int i_lim, i_count;
...
i_count = atoi(argv[1]);
time(&upto.tv_sec);
upto.tv_sec++;
upto.tv_nsec = 0;
i_lim = 0;
do
{
//fill next packet
//send next packet
i_count--;
i_lim++;
if ( time(NULL) >= upto.tv_sec )
{
time(&upto.tv_sec);
upto.tv_sec++;
upto.tv_nsec = 0;
i_lim = 0;
}
if ( i_lim > 3000 )
{
nanosleep(&upto,&remain);
time(&upto.tv_sec);
upto.tv_sec++;
upto.tv_nsec = 0;
i_lim = 0;
}
} while ( i_count > 0 );

Időnként, beáll és vár!?

UI: miért nem tartja a formátumot a code elem sem?

* Én egy indián vagyok. Minden indián hazudik.

Jó .( Már tudom relatív, idő kell nem abszolút :( Már csak az a gond, hogy honnan is? Mi adja meg nekem a kezdettől eltelt időt nsec -ben avagy mi tölti ki nekem a timespec struktúrát?

* Én egy indián vagyok. Minden indián hazudik.

No, hát megtaláltam, a "clock_gettime" rutint - már csak azt nem értem, hogy akkor ez most nem a glibc része, mert a dokumentációban egy szóval nem említik. A /usr/include/time.h -ban azonban benne van, illetve a linkelésnél kell az "rt" könyvtár (-lrt).
Azonban a kódom még mindíg nem működik jól :( Valamit nagyon benézek :(
Egy biztos a feladat nem is olyan triviális, azaz be kell korlátoznom, mondjuk max 3000 csomag/sec -re.

* Én egy indián vagyok. Minden indián hazudik.

Valami már alakul, de nem az igazi :(
(Bocs de még mindig nem tudom hogy kell megadni az hogy ne törölje a sor eleji space -ket :()

struct timespec wait,remaind;
long long int elapsed, tick_start;
...
i_lim = 0;
start_tick = tick_nsec();
do
{
if ( ++ i_lim > 300 )
{
elapsed = tick_nsec() - tick_start;
if ( elapsed < 100000000L && elapsed > 1000000L )
{
wait.tv_sec = 0;
wait.tv_nsec = 100000000L - elapsed;
nanosleep(&wait,&remain);
}
start_tick = tick_nsec();
i_lim = 0;
}
// itt összeállítom és elküldöm a csomagot
} while ...

long long int tick_nsec()
{
struct timespec tspec;
clock_gettime(CLOCK_REALTIME,&tspec);
return ( (long long int)(tspec.tv_sec * 1000000000L + tspec.tv_nsec) )
}

Sajnos ez sem tökéletes, a windows oldal 100 ms -ként nézi meg a puffert, és általában kb. 300 csomagot kezel le, de van hogy több mint 600 -at, és a következő ciklusban nem jön semmi!?

* Én egy indián vagyok. Minden indián hazudik.