28 aprilie 2010

Socketi "raw"

Socketii "raw", din familia de protocoale PF_PACKET sunt folositi pentru a receptiona sau trimite pachete "raw" direct la nivelul dispozitivului fizic (nivelul OSI 2). Sunt folositi pentru a implementa protocoale direct in user-space fie pentru a le testa fie pentru ca protocolul nu are rost sa fie implementat in kernel. Aceasta familie de protocoale pune la dispozitie doua tipuri de socketi:

  • SOCK_RAW - pachetele sunt prezentate utilizatorului asa cum sunt primite de la device driver (i.e. cu tot cu headerul de nivel 2); de asemenea la trimiterea unui pachet printr-un socket de acest tip pachetele sunt trimise nemodificate astfel incat trebuie sa contina si headerul
  • SOCK_DGRAM - pachetele sunt prezentate utilizatorului fara headerul de nivel 2; pachetelor trimise pritr-un socket de acest tip li se va adauga headerul de nivel 2; practic utilizatorul vede doar continul pachetului

Socketii "raw" sunt creati la fel ca si socketii normali cu apelul de sistem socket:

packet_socket = socket(PF_PACKET, int socket_type, int protocol);
unde socket_type poate fi SOCK_RAW sau SOCK_DGRAM iar protocol una din constantele definite in linux/if_ether.

Aplicatia de mai jos "asculta" toate pachetele care ajung in calculator si printeaza continutul lor, fie ca ii sunt adresate calculatorului sau nu.


#include "netinet/in.h"
#include "stdio.h"
#include "unistd.h"
#include "netpacket/packet.h"
#include "sys/types.h"
#include "sys/socket.h"
#include "net/if.h"
#include "stdlib.h"
#include "sys/ioctl.h"
#include "string.h"
#include "net/if_arp.h"
#include "linux/if_ether.h"

int start(int proto) //eth_p_ip
{
int fd = socket(PF_PACKET, SOCK_DGRAM, htons(proto));

if (fd < 0)
{
perror("can't create protocol socket:");
return -1;
}
return fd;
}

int bind_socket(int fd, int interface, int protocol)
{
struct sockaddr_ll sock_info;

memset(&sock_info, 0, sizeof(sock_info));

sock_info.sll_family = AF_PACKET;
sock_info.sll_protocol = htons(protocol);
sock_info.sll_ifindex = interface;

if (bind(fd, (struct sockaddr *)&sock_info, sizeof(sock_info)))
{
perror("can't bind socket to i/f %m\n");
return -1;
}
return 0;
}

int find_interface(char *name)
{
struct ifreq ifr;
int iindex = 1;
int sock = socket(PF_PACKET, SOCK_RAW, 0);

ifr.ifr_ifindex = iindex;

while (ioctl(sock, SIOCGIFNAME, &ifr) == 0)
{
if (strcmp(ifr.ifr_name, name) == 0)
{
close(sock);
return iindex;
}
ifr.ifr_ifindex = ++iindex;
}
close(sock);
return -1;
}

int set_promisc(int fd, int ifn)
{
struct packet_mreq pack_info;

pack_info.mr_type = PACKET_MR_PROMISC;
pack_info.mr_alen = 0;

pack_info.mr_ifindex = ifn;

if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &pack_info, sizeof(pack_info)))
{
printf("can't set promiscous mode\n");
return -1;
}

return 0;
}

int recv_packet(int sockfd, int *ifn, unsigned char macaddr[], unsigned char *data, int maxlen)
{
struct msghdr msg;
struct iovec iov;
struct sockaddr_ll sock_info;

int len;

memset(&msg, 0, sizeof(msg));
msg.msg_name = &sock_info;
msg.msg_namelen = sizeof(sock_info);
msg.msg_iovlen = 1;
msg.msg_iov = &iov;

iov.iov_len = maxlen;

iov.iov_base = data;

len = recvmsg(sockfd, &msg, 0);

*ifn = sock_info.sll_ifindex;
memcpy(macaddr, sock_info.sll_addr, 6);

return len;
}


int main()
{
int sockfd = start(ETH_P_IP);
int interface = find_interface("eth0");
int b = bind_socket(sockfd, interface, ETH_P_IP);
unsigned char *macaddr=(unsigned char *)malloc(256*sizeof(unsigned char)), *data= (unsigned char *)malloc(256*sizeof(unsigned char));
while (1) {
int promise = set_promisc(sockfd, interface);
int recv = recv_packet(sockfd, &interface, macaddr, data, 256), i;
for(i=0; i < 256;i++)
printf("%c",data[i]);
printf("\n");
}
return 0;

}

Nota
Fisierul va fi executat din pozitie de admin (in Linux: sudo su)

Niciun comentariu: