鏈路層原始套接字程式設計-總結
2、預設情況下網絡卡只處理目的地址是本機網絡卡地址的包,可通過設定混雜模式,使網絡卡將收到的所有包(包括組播和廣播)都轉發給作業系統。程式碼如下:
struct ifreq ifr;
strcpy(ifr.ifr_name, if_name);
ioctl(fd, SIOCGIFFLAGS, &ifr);
ifr.ifr_flags |= IFF_PROMISC;
ioctl(fd, SIOCSIFFLAGS, &ifr);
3、對於多網絡卡系統,作業系統在收包時不區分是從哪個網絡卡收到的,統一轉發給使用者程序socket,特別的,當用戶程序建立了原始套接字socket,那麼作業系統在轉發訊息時,將從網絡卡收到的buf複製給所有的、關心的原始套接字(原因是作業系統不知道怎麼區分不同的原始套接字的包)。
4、可通過bind函式將建立的原始套接字繫結到指定的addr,addr的實際型別為struct sockaddr_ll,繫結時需要設定sll_family,sll_protocol,sll_ifindex這幾個引數。其中,sll_ifindex為指定的介面名稱的索引,可通過ioctl函式獲取ioctl(fd, SIOCGIFINDEX, &ifr);
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
struct sockaddr_ll{
unsigned short int sll_family;
unsigned short int sll_protocol;
int sll_ifindex;
unsigned short int sll_hatype;
unsigned char sll_pkttype;
unsigned char sll_halen;
unsigned char sll_addr[8];
};
5、sendto函式在傳送鏈路層資料時,需要自己組織buf內容,包括乙太網頭和協議內容。其中dest_addr引數必須設定,與應用層sendto函式指定目的地址不同,鏈路層傳送時需要指定將buf從本機的哪個網絡卡傳送出去。同樣,實際型別為struct sockaddr_ll,只需要設定sll_ifindex引數即可。
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
6、recvfrom函式在接收資料時,如果本機的任意一個網絡卡設定了混雜模式,那麼這個函式都能收到鏈路層包,除非sockfd採用bind函式綁定了網絡卡。如果繫結的網絡卡設定了混雜模式,則只能收到發往本網絡卡包(包括組播包和廣播包等)。其中,src_addr為傳送包的源地址。
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
7、應用層socket通過(源IP、源埠、目的IP、目的埠)四元組來確定socket對應的程序,作業系統在轉發包時能夠確定唯一的程序ID;而原始套接字socket沒有埠的概念,所以只能通過(源IP、目的IP)或(源MAC、目的MAC)二元組來區分不同的程序。ping程式通過在協議欄位裡新增程序ID來區分;而Y1731協議裡沒有設定這個欄位的地方。