15 Kasım 2017 Çarşamba

PF_PACKET socketi

Giriş
PF_PACKET (eski kodlarda AF_PACKET olarak geçer) socketi Linux'a mahsustur. Ethernet seviyesindeki paketler alıp gönderebilmemizi sağlar.
socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)
socket metodu
1. Parametre
PF_PACKET yerine eski kodlarda SOCK_PACKET sabiti de görülebilir.

2. Parametre
SOCK_DGRAM veya SOCK_RAW olabilir. SOCK_DGRAM ise paketi gönderirken ethernet header bizim için doldurulur. SOCK_RAW ise ethernet header'ını doldurmamız gerekir. Aslınad bu kısmı hiç SOCK_DGRAM olarak kullanmadım. Bu yüzden emin değilim.

3. Parametre
Hangi tip ethernet paketlerini almak istediğimizi belirtiriz. Ethernet frame type ta diyebiliriz. Bu alan Big Endian formatındadır. 2 byte uzunluğundadır ve genellikle 0x800'den başlar.
IP paketleri için Wireshark şunu gösterir.
Type: IP (0x0800)

Örnek
Şöyle yaparız
fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); //Tüm paketler
fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));  //Incoming IP paketleri
ETH_P_ALL ile tüm ethernet paketleri alınır.
ETH_P_IP ile IPv4 paketleri alınır.
ETH_P_IPV6, ETH_P_ARP gibi IPv6 ve ARP paketlerini alma seçenekleri de var.

Örnek
Şöyle yaparız.
if ((sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))) == -1) {
  perror("socket");
  exit(1);
}

bind metodu
Eğer sadece belli bir arayüzden paket yakalamak istersek socketi bind etmek gerekir. sockaddr_ll veriyapısı doldurulur ve bind metodu çağrılır. sockaddr_ll içine family olarak PF_PACKET ifindex olarak ise bind edilmek istenen arayüzün numarası atanır. Arayüzün numarası eğer bilinmiyorsa ismi kullanılarak ioctl + SIOCGIFINDEX ile bulunabilir.

Örnek
Şöyle yaparız.
void BindToInterface(int raw , char *device , int protocol) { 
  struct sockaddr_ll sll;
  struct ifreq ifr;
  bzero(&sll , sizeof(sll));
  bzero(&ifr , sizeof(ifr)); 
  strncpy((char *)ifr.ifr_name ,device , IFNAMSIZ); 
  //copy device name to ifr 
  if((ioctl(raw , SIOCGIFINDEX , &ifr)) == -1)
  { 
    perror("Unable to find interface index");
    exit(-1); 
  }
  sll.sll_family = AF_PACKET; 
  sll.sll_ifindex = ifr.ifr_ifindex; 
  sll.sll_protocol = htons(protocol); 
  if((bind(raw , (struct sockaddr *)&sll , sizeof(sll))) ==-1)
  {
    perror("bind: ");
    exit(-1);
  }
  return 0;
} 
Örnek
Şöyle yaparız.
sockaddr_ll sll;
memset(&sll, 0, sizeof(sockaddr));
sockaddr.sll_family = PF_PACKET;
sockaddr.sll_protocol = htons(d_ethtype)
sockaddr.sll_ifindex = if_nametoindex(d_receive_iface.c_str());
sockaddr.sll_hatype = 1;
Promiscuous Mode
Ethernet kartları kendi MAC adreslerine gönderilmemiş paketleri içeri almazlar. Yukarıdaki seçeneklerin çalışması için ethernet kartının promiscuous moda sokulması gerekir. Bunun için yeni kodlarda PACKET_ADD_MEMBERSHIP kullanılır.Örnek
Şöyle yaparız.
struct packet_mreq mr;
memset(&mr, 0, sizeof(mr));
mr.mr_ifindex = interface_index;
mr.mr_type = PACKET_MR_PROMISC;

setsockopt(socFd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr);
Örnek
Eski kodlarda ioctl ve SIOCSIFFLAGS kullanılır. Eski kod şöyledir.
struct ifreq ifr;
memset(&ifr, 0, sizeof (struct ifreq));

//Interface adını ver
strncpy(ifr.ifr_name, device, strlen(device) + 1);

//MEvcut flag değerlerini al
ioctl(m_sockfd, SIOCGIFFLAGS, &ifr);

//Yeni değerleri ata
ifr.ifr_flags |= IFF_PROMISC;
ioctl(m_sockfd, SIOCSIFFLAGS, &ifr);

Hiç yorum yok:

Yorum Gönder