2014年12月10日 星期三

packet socket

收送 L2 封包要用 packet socket,使用
socket(AF_PACKET, SOCK_RAW, protocol) 或
socket(AF_PACKET, SOCK_DGRAM, protocol)
建立一個通訊端點後回傳 file descriptor。SOCK_RAW 是收送整個 L2 封包,SOCK_DGRAM 則不包括 L2 header。protocol 是指定要收的 Ether Type (需轉成 network endian),如果所有 Ether Type 都收的話用 htons(ETH_P_ALL)。

預設所有網路界面都收,可用 bind() 綁定界面。收的封包包括 unicast 及 broadcast,用 setsockopt(<fd>, SOL_PACKET, <optname>, <optval>, <optlen>) 修改是否接收其它 MAC 位址的封包。
  • PACKET_ADD_MEMBERSHIP, PACKET_DROP_MEMBERSHIP:
    • PACKET_MR_PROMISC:不做 MAC address 過濾
    • PACKET_MR_MULTICAST:增減 multicast 位址
    • PACKET_MR_ALLMULTI:接收所有 multicast
網路界面預設只收 boardcast 及符合介面 MAC 位址的 unicast,但設為 promiscous 模式時所有封包都會進來,要做進一步過濾可以用 socket filter 過濾目的 MAC 位址

int da_filter(int sk, const unsigned char *mac)
{
         struct sock_filter code[] = {
                /* tcpdump -dd ether dst 12:34:56:78:9a:bc or broadcast
                 * (000) ld       [2]
                 * (001) jeq      #0x56789abc      jt 2 jf 4
                 * (002) ldh      [0]
                 * (003) jeq      #0x1234          jt 7 jf 8
                 * (004) jeq      #0xffffffff      jt 5 jf 8
                 * (005) ldh      [0]
                 * (006) jeq      #0xffff          jt 7 jf 8
                 * (007) ret      #65535
                 * (008) ret      #0
                 */
                { 0x20, 0, 0, 0x00000002 },
                { 0x15, 0, 2, 0x56789abc },
                { 0x28, 0, 0, 0x00000000 },
                { 0x15, 3, 4, 0x00001234 },
                { 0x15, 0, 3, 0xffffffff },
                { 0x28, 0, 0, 0x00000000 },
                { 0x15, 0, 1, 0x0000ffff },
                { 0x6, 0, 0, 0x0000ffff },
                { 0x6, 0, 0, 0x00000000 },
        };
        code[3].k = (mac[0]<<8)+mac[1];
        code[1].k = (mac[2]<<24)+(mac[3]<<16)+(mac[4]<<8)+mac[5];
        struct sock_fprog bpf = {
                .len = sizeof(code)/sizeof(code[0]),
                .filter = code,
        };
        if ((ret = setsockopt(sk, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf))))
                printf("setsockopt error: %s\n", strerror(errno));
 return ret;
}
透過 SO_DETACH_FILTER 或 socket 關閉時移除。改變需要先移除舊的,如果新的加入失敗會沿用舊的。SO_LOCK_FILTER 選項鎖定 attached 的過濾碼,不能移除或改變。

收封包送封包
read(fd, buf, len)write(fd, buf, len)
recv(fd, buf, len, flags)send(fd, buf, len, flags)
recvfrom(fd, buf, len, flags, addr, addrlen)sendto(fd, buf, len, flags, addr, addrlen)
recvmsg(fd, msg, flags)sendmsg(fd, msg, flags)

如果要用 write 或 send 送封包,需要先綁定網路界面,不然不知道要從哪個界面送。


bindsendreceive
familyvv?
protocolv-?
ifindexvv?
hatype--v
pktype--v
halen-v?
addr[8]-v?

註:

參考來源
  1. man packet
  2. man socket
  3. http://yusufonlinux.blogspot.tw/2010/11/data-link-access-and-zero-copy.html
  4. http://www.scs.stanford.edu/histar/src/lind/os-Linux/drivers/rawsock_user.c
  5. https://www.kernel.org/doc/Documentation/networking/filter.txt

沒有留言:

張貼留言

SIP header Via

所有 SIP 訊息 都要有 Via,縮寫 v。一開始的 UAC 和後續途經的每個 proxy 都會疊加一個 Via 放傳送的位址,依序作為回應的路徑。 格式 sent-protocol sent-by [ ;branch= branch ][ ; 參數 ...] s...