2014-06-22 105 views
7

我已經寫了一個C程序,它將以太網幀直接寫入線路(運行於發送方或接收方兩種模式)。 發送端發送帶有兩個VLAN標籤(QinQ)的幀,但奇怪的是當幀到達接收端時,ethertype已經改變爲標準(單個)VLAN封裝幀。 NIC是否可以這樣做,還是Linux不允許這樣做? Wireshark顯示與tcpdump相同的行爲。Linux:在發送以太網幀時,ethertype正在被重寫

要解釋下面的圖像發送者​​正在發送幀到以太網廣播地址FF:FF:FF:FF:FF找到接收器(這是兩臺測試機器通過交叉電纜連接,但下面的結果是與交換機或集線器相同)。正如你可以看到這些幀帶有兩個VLAN標籤,外層標籤的ethertype爲0x8100,VLAN ID爲40,內層VLAN的類型爲0x8100,VLAN ID爲20.我們都知道,對於QinQ幀,外部幀應該有一個0x88a8的ethertype!

當我的應用程序中的發送者發送幀時,它們確實具有0x88a8的外部ethertype類型,但是按照下面的圖片,它們在內部和外部ethertypes上都以0x8100接收。突出顯示的文本是接收方回覆回覆,如您所見,幀的外框爲0x88a8,內部爲0x8100。另一臺機器上的tcpdump顯示相同(它是相同的代碼!幀以0x88a8外部0x8100內部發送,但始終以0x8100外部和0x8100內部接收)。

enter image description here

void BuildHeaders(char* &txBuffer, unsigned char (&destMAC)[6], 
    unsigned char (&sourceMAC)[6], short &PCP, short &vlanID, 
    short &qinqID, short &qinqPCP, int &headersLength) 
{ 

int offset = 0; 

short TPI = 0; 
short TCI = 0; 
short *p = &TPI; 
short *c = &TCI; 
short vlanIDtemp; 

// Copy the destination and source MAC addresses 
memcpy((void*)txBuffer, (void*)destMAC, ETH_ALEN); 
memcpy((void*)(txBuffer+ETH_ALEN), (void*)sourceMAC, ETH_ALEN); 
offset = (ETH_ALEN*2); 

// Add on the QinQ Tag Protocol Identifier 
vlanIDtemp = qinq 
TPI = htons(0x88a8); //0x88a8 == IEEE802.1ad, 0x9100 == older IEEE802.1QinQ 
memcpy((void*)(txBuffer+offset), p, 2); 
offset+=2; 

// Now build the QinQ Tag Control Identifier: 
TCI = (qinqPCP & 0x07) << 5; 
qinqID = qinqID >> 8; 
TCI = TCI | (qinqID & 0x0f); 
qinqID = vlanIDtemp; 
qinqID = qinqID << 8; 
TCI = TCI | (qinqID & 0xffff); 

memcpy((void*)(txBuffer+offset), c, 2); 
offset+=2; 

// VLAN headers 
vlanIDtemp = vlanID; 
TPI = htons(0x8100); 
memcpy((void*)(txBuffer+offset), p, 2); 
offset+=2; 

TCI = (PCP & 0x07) << 5; 
vlanID = vlanID >> 8; 
TCI = TCI | (vlanID & 0x0f); 
vlanID = vlanIDtemp; 
vlanID = vlanID << 8; 
TCI = TCI | (vlanID & 0xffff); 

memcpy((void*)(txBuffer+offset), c, 2); 
offset+=2; 

// Push on the Ethertype (IPv4) for the payload 
TPI = htons(0x0800); 
memcpy((void*)(txBuffer+offset), p, 2); 
offset+=2; 

headersLength = offset; 

} 

sendResult = sendto(sockFD, txBuffer, fSizeTotal, 0, (struct sockaddr*)&socket_address, sizeof(socket_address)); 
+2

使用'ethtool'確保您的NIC上禁用了任何VLAN卸載功能。另外,你如何打開'sockFD'? –

+0

'sockFD = socket(AF_PACKET,SOCK_RAW,htons(ETH_P_ALL));' – jwbensley

+0

我會檢查ethtool輸出下一次我得到和更新的機會。 – jwbensley

回答

11

(完全重寫,以簡化的答案。我在C頭文件和源文件也固定了不少的bug列表如下。)

有在linux-netdev mailing list討論about exactly this在2014年4月,主題「802.1AD數據包 - 內核將所有數據包中的ether類型從88A8更改爲8100」

事實證明,內核不會改變以太類型,它只是在接收數據包時消耗它。在下面的示例中,考慮到最近有足夠的內核,它正確地用於VLAN路由(包括802.1AD和802.1Q VLAN的單獨規則)。即使VLAN標籤沒有用於路由(例如,如果沒有配置VLAN,或者未加載8021q內核模塊),內核也會使用VLAN標記。

因此,原來的問題,「當發送以太網幀的以太類型正在重新寫爲」,是不正確:以太網類型沒有被改寫。內核消耗的內存爲

因爲內核使用了VLAN標記,libpcap - 這是tcpdump使用的數據包捕獲庫,wireshark et al。 - 試圖將其重新引入數據包標題。不幸的是,它總是使用802.1Q VLAN報頭(8100)。

有一個libpcap的suggested change,它解決了libpcap中的這個問題,但是在撰寫本文時,它似乎還沒有被包含在內;您仍然可以在libpcap source file for Linux的幾個地方看到htons(ETH_P_8021Q)硬編碼。


我不能假設你會接受我的話,所以讓我告訴你如何確定你自己。

讓我們編寫一個簡單的數據包發送者和接收者,它直接使用內核接口,而不需要libpcap的幫助。

rawpacket.h:

#ifndef RAWPACKET_H 
#define RAWPACKET_H 
#include <unistd.h> 
#include <sys/socket.h> 
#include <sys/ioctl.h> 
#include <netpacket/packet.h> 
#include <net/ethernet.h> 
#include <net/if.h> 
#include <arpa/inet.h> 
#include <linux/if_ether.h> 
#include <string.h> 
#include <errno.h> 
#include <stdio.h> 

static int rawpacket_socket(const int protocol, 
          const char *const interface, 
          void *const hwaddr) 
{ 
    struct ifreq  iface; 
    struct sockaddr_ll addr; 
    int     socketfd, result; 
    int     ifindex = 0; 

    if (!interface || !*interface) { 
     errno = EINVAL; 
     return -1; 
    } 

    socketfd = socket(AF_PACKET, SOCK_RAW, htons(protocol)); 
    if (socketfd == -1) 
     return -1; 

    do { 

     memset(&iface, 0, sizeof iface); 
     strncpy((char *)&iface.ifr_name, interface, IFNAMSIZ); 
     result = ioctl(socketfd, SIOCGIFINDEX, &iface); 
     if (result == -1) 
      break; 
     ifindex = iface.ifr_ifindex; 

     memset(&iface, 0, sizeof iface); 
     strncpy((char *)&iface.ifr_name, interface, IFNAMSIZ); 
     result = ioctl(socketfd, SIOCGIFFLAGS, &iface); 
     if (result == -1) 
      break; 
     iface.ifr_flags |= IFF_PROMISC; 
     result = ioctl(socketfd, SIOCSIFFLAGS, &iface); 
     if (result == -1) 
      break; 

     memset(&iface, 0, sizeof iface); 
     strncpy((char *)&iface.ifr_name, interface, IFNAMSIZ); 
     result = ioctl(socketfd, SIOCGIFHWADDR, &iface); 
     if (result == -1) 
      break; 

     memset(&addr, 0, sizeof addr); 
     addr.sll_family = AF_PACKET; 
     addr.sll_protocol = htons(protocol); 
     addr.sll_ifindex = ifindex; 
     addr.sll_hatype = 0; 
     addr.sll_pkttype = 0; 
     addr.sll_halen = ETH_ALEN; /* Assume ethernet! */ 
     memcpy(&addr.sll_addr, &iface.ifr_hwaddr.sa_data, addr.sll_halen); 
     if (hwaddr) 
      memcpy(hwaddr, &iface.ifr_hwaddr.sa_data, ETH_ALEN); 

     if (bind(socketfd, (struct sockaddr *)&addr, sizeof addr)) 
      break; 

     errno = 0; 
     return socketfd; 

    } while (0); 

    { 
     const int saved_errno = errno; 
     close(socketfd); 
     errno = saved_errno; 
     return -1; 
    } 
} 

static unsigned int tci(const unsigned int priority, 
         const unsigned int drop, 
         const unsigned int vlan) 
{ 
    return (vlan & 0xFFFU) 
     | ((!!drop) << 12U) 
     | ((priority & 7U) << 13U); 
} 

static size_t rawpacket_qinq(unsigned char *const buffer, size_t const length, 
          const unsigned char *const srcaddr, 
          const unsigned char *const dstaddr, 
          const unsigned int service_tci, 
          const unsigned int customer_tci, 
          const unsigned int ethertype) 
{ 
    unsigned char *ptr = buffer; 
    uint32_t  tag; 
    uint16_t  type; 

    if (length < 2 * ETH_ALEN + 4 + 4 + 2) { 
     errno = ENOSPC; 
     return (size_t)0; 
    } 

    memcpy(ptr, dstaddr, ETH_ALEN); 
    ptr += ETH_ALEN; 

    memcpy(ptr, srcaddr, ETH_ALEN); 
    ptr += ETH_ALEN; 

    /* Service 802.1AD tag. */ 
    tag = htonl(((uint32_t)(ETH_P_8021AD) << 16U) 
       | ((uint32_t)service_tci & 0xFFFFU)); 
    memcpy(ptr, &tag, 4); 
    ptr += 4; 

    /* Client 802.1Q tag. */ 
    tag = htonl(((uint32_t)(ETH_P_8021Q) << 16U) 
       | ((uint32_t)customer_tci & 0xFFFFU)); 
    memcpy(ptr, &tag, 4); 
    ptr += 4; 

    /* Ethertype tag. */ 
    type = htons((uint16_t)ethertype); 
    memcpy(ptr, &type, 2); 
    ptr += 2; 

    return (size_t)(ptr - buffer); 
} 

#endif /* RAWPACKET_H */ 

sender.c:

#include <string.h> 
#include <errno.h> 
#include <stdio.h> 
#include "rawpacket.h" 

static size_t parse_data(unsigned char *const data, const size_t size, 
         const char *const string) 
{ 
    char *ends = strncpy((char *)data, string, size); 
    return (size_t)(ends - (char *)data); 
} 


static int parse_hwaddr(const char *const string, 
         void *const hwaddr) 
{ 
    unsigned int addr[6]; 
    char   dummy; 

    if (sscanf(string, " %02x:%02x:%02x:%02x:%02x:%02x %c", 
         &addr[0], &addr[1], &addr[2], 
         &addr[3], &addr[4], &addr[5], 
         &dummy) == 6 || 
     sscanf(string, " %02x%02x%02x%02x%02x%02x %c", 
         &addr[0], &addr[1], &addr[2], 
         &addr[3], &addr[4], &addr[5], 
         &dummy) == 6) { 
     if (hwaddr) { 
      ((unsigned char *)hwaddr)[0] = addr[0]; 
      ((unsigned char *)hwaddr)[1] = addr[1]; 
      ((unsigned char *)hwaddr)[2] = addr[2]; 
      ((unsigned char *)hwaddr)[3] = addr[3]; 
      ((unsigned char *)hwaddr)[4] = addr[4]; 
      ((unsigned char *)hwaddr)[5] = addr[5]; 
     } 
     return 0; 
    } 

    errno = EINVAL; 
    return -1; 
} 

int main(int argc, char *argv[]) 
{ 
    unsigned char packet[ETH_FRAME_LEN + ETH_FCS_LEN]; 
    unsigned char srcaddr[6], dstaddr[6]; 
    int   socketfd; 
    size_t  size, i; 
    ssize_t  n; 

    if (argc < 3 || argc > 4 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { 
     fprintf(stderr, "\n"); 
     fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]); 
     fprintf(stderr, "  %s interface hwaddr [message]\n", argv[0]); 
     fprintf(stderr, "\n"); 
     return 1; 
    } 

    if (parse_hwaddr(argv[2], &dstaddr)) { 
     fprintf(stderr, "%s: Invalid destination hardware address.\n", argv[2]); 
     return 1; 
    } 

    socketfd = rawpacket_socket(ETH_P_ALL, argv[1], &srcaddr); 
    if (socketfd == -1) { 
     fprintf(stderr, "%s: %s.\n", argv[1], strerror(errno)); 
     return 1; 
    } 

    memset(packet, 0, sizeof packet); 

    /* Construct a QinQ header for a fake Ethernet packet type. */ 
    size = rawpacket_qinq(packet, sizeof packet, srcaddr, dstaddr, 
            tci(7, 0, 1U), tci(7, 0, 2U), 
            ETH_P_IP); 
    if (!size) { 
     fprintf(stderr, "Failed to construct QinQ headers: %s.\n", strerror(errno)); 
     close(socketfd); 
     return 1; 
    } 

    /* Add packet payload. */ 
    if (argc > 3) 
     size += parse_data(packet + size, sizeof packet - size, argv[3]); 
    else 
     size += parse_data(packet + size, sizeof packet - size, "Hello!"); 

    /* Pad with zeroes to minimum 64 octet length. */ 
    if (size < 64) 
     size = 64; 

    /* Send it. */ 
    n = send(socketfd, packet, size, 0); 
    if (n == -1) { 
     fprintf(stderr, "Failed to send packet: %s.\n", strerror(errno)); 
     shutdown(socketfd, SHUT_RDWR); 
     close(socketfd); 
     return 1; 
    } 

    fprintf(stderr, "Sent %ld bytes:", (long)n); 
    for (i = 0; i < size; i++) 
     fprintf(stderr, " %02x", packet[i]); 
    fprintf(stderr, "\n"); 
    fflush(stderr); 

    shutdown(socketfd, SHUT_RDWR); 
    if (close(socketfd)) { 
     fprintf(stderr, "Error closing socket: %s.\n", strerror(errno)); 
     return 1; 
    } 

    return 0; 
} 

receiver.c:

#include <sys/types.h> 
#include <sys/socket.h> 
#include <string.h> 
#include <signal.h> 
#include <errno.h> 
#include <stdio.h> 
#include "rawpacket.h" 

static volatile sig_atomic_t done = 0; 

static void handle_done(int signum) 
{ 
    done = signum; 
} 

static int install_done(const int signum) 
{ 
    struct sigaction act; 
    sigemptyset(&act.sa_mask); 
    act.sa_handler = handle_done; 
    act.sa_flags = 0; 
    if (sigaction(signum, &act, NULL)) 
     return errno; 
    return 0; 
} 

static const char *protocol_name(const unsigned int protocol) 
{ 
    static char buffer[16]; 
    switch (protocol & 0xFFFFU) { 
    case 0x0001: return "ETH_P_802_3"; 
    case 0x0002: return "ETH_P_AX25"; 
    case 0x0003: return "ETH_P_ALL"; 
    case 0x0060: return "ETH_P_LOOP"; 
    case 0x0800: return "ETH_P_IP"; 
    case 0x0806: return "ETH_P_ARP"; 
    case 0x8100: return "ETH_P_8021Q (802.1Q VLAN)"; 
    case 0x88A8: return "ETH_P_8021AD (802.1AD VLAN)"; 
    default: 
     snprintf(buffer, sizeof buffer, "0x%04x", protocol & 0xFFFFU); 
     return (const char *)buffer; 
    } 
} 

static const char *header_type(const unsigned int hatype) 
{ 
    static char buffer[16]; 
    switch (hatype) { 
    case 1: return "ARPHRD_ETHER: Ethernet 10Mbps"; 
    case 2: return "ARPHRD_EETHER: Experimental Ethernet"; 
    case 768: return "ARPHRD_TUNNEL: IP Tunnel"; 
    case 772: return "ARPHRD_LOOP: Loopback"; 
    default: 
     snprintf(buffer, sizeof buffer, "0x%04x", hatype); 
     return buffer; 
    } 
} 

static const char *packet_type(const unsigned int pkttype) 
{ 
    static char buffer[16]; 
    switch (pkttype) { 
    case PACKET_HOST:  return "PACKET_HOST"; 
    case PACKET_BROADCAST: return "PACKET_BROADCAST"; 
    case PACKET_MULTICAST: return "PACKET_MULTICAST"; 
    case PACKET_OTHERHOST: return "PACKET_OTHERHOST"; 
    case PACKET_OUTGOING: return "PACKET_OUTGOING"; 
    default: 
     snprintf(buffer, sizeof buffer, "0x%02x", pkttype); 
     return (const char *)buffer; 
    } 
} 

static void fhex(FILE *const out, 
       const char *const before, 
       const char *const after, 
       const void *const src, const size_t len) 
{ 
    const unsigned char *const data = src; 
    size_t i; 

    if (len < 1) 
     return; 

    if (before) 
     fputs(before, out); 

    for (i = 0; i < len; i++) 
     fprintf(out, " %02x", data[i]); 

    if (after) 
     fputs(after, out); 
} 

int main(int argc, char *argv[]) 
{ 
    struct sockaddr_ll addr; 
    socklen_t   addrlen; 
    unsigned char  data[2048]; 
    ssize_t    n; 
    int     socketfd, flag; 

    if (argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { 
     fprintf(stderr, "\n"); 
     fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]); 
     fprintf(stderr, "  %s interface\n", argv[0]); 
     fprintf(stderr, "\n"); 
     return 1; 
    } 

    if (install_done(SIGINT) || 
     install_done(SIGHUP) || 
     install_done(SIGTERM)) { 
     fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno)); 
     return 1; 
    } 

    socketfd = rawpacket_socket(ETH_P_ALL, argv[1], NULL); 
    if (socketfd == -1) { 
     fprintf(stderr, "%s: %s.\n", argv[1], strerror(errno)); 
     return 1; 
    } 

    flag = 1; 
    if (setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof flag)) { 
     fprintf(stderr, "Cannot set REUSEADDR socket option: %s.\n", strerror(errno)); 
     close(socketfd); 
     return 1; 
    } 

    if (setsockopt(socketfd, SOL_SOCKET, SO_BINDTODEVICE, argv[1], strlen(argv[1]) + 1)) { 
     fprintf(stderr, "Cannot bind to device %s: %s.\n", argv[1], strerror(errno)); 
     close(socketfd); 
     return 1; 
    } 

    while (!done) { 

     memset(data, 0, sizeof data); 
     memset(&addr, 0, sizeof addr); 
     addrlen = sizeof addr; 
     n = recvfrom(socketfd, &data, sizeof data, 0, 
        (struct sockaddr *)&addr, &addrlen); 
     if (n == -1) { 
      if (errno == EINTR) 
       continue; 
      fprintf(stderr, "Receive error: %s.\n", strerror(errno)); 
      break; 
     } 

     printf("Received %d bytes:\n", (int)n); 
     printf("\t Protocol: %s\n", protocol_name(htons(addr.sll_protocol))); 
     printf("\t Interface: %d\n", (int)addr.sll_ifindex); 
     printf("\t Header type: %s\n", header_type(addr.sll_hatype)); 
     printf("\t Packet type: %s\n", packet_type(addr.sll_pkttype)); 
     fhex(stdout, "\t  Address:", "\n", addr.sll_addr, addr.sll_halen); 
     fhex(stdout, "\t  Data:", "\n", data, n); 
     printf("\n"); 

     fflush(stdout); 
    } 

    shutdown(socketfd, SHUT_RDWR); 
    close(socketfd); 
    return 0; 
} 

進行編譯,您可以使用

gcc -O2 receiver.c -o receiver 
gcc -O2 sender.c -o sender 

以無參數運行或使用-h運行,以查看任何一個的用法。 sender只發送一個數據包。 receiver在指定接口(在混雜模式下)監聽,直到您中斷它(Ctrl + C)或發送它TERM信號。

啓動接收器中的環回接口上的一個虛擬終端:

sudo ./receiver lo 

在同一臺機器上的另一虛擬終端,運行

sudo ./sender lo FF:FF:FF:FF:FF:FF '_The contents of a 64-byte Ethernet frame_' 

將輸出(新行和縮進添加爲了便於理解)

Sent 64 bytes: ff ff ff ff ff ff 
       00 00 00 00 00 00 
       88 a8 e0 01 
       81 00 e0 02 
       08 00 
       5f 54 68 65 20 63 6f 6e 
       74 65 6e 74 73 20 6f 66 
       20 61 20 36 34 2d 62 79 
       74 65 20 45 74 68 65 72 
       6e 65 74 20 66 72 61 6d 
       65 5f 

在接收終端中,howe ver,我們看到(換行符和縮進添加):

Received 64 bytes: 
    Protocol: ETH_P_ALL 
    Interface: 1 
Header type: ATPHRD_LOOP: Loopback 
Packet type: PACKET_OUTGOING 
    Address: 00 00 00 00 00 00 
     Data: ff ff ff ff ff ff 
       00 00 00 00 00 00 
       88 a8 e0 01 
       81 00 e0 02 
       08 00 
       5f 54 68 65 20 63 6f 6e 
       74 65 6e 74 73 20 6f 66 
       20 61 20 36 34 2d 62 79 
       74 65 20 45 74 68 65 72 
       6e 65 74 20 66 72 61 6d 
       65 5f 

Received 60 bytes: 
    Protocol: ETH_P_8021Q (802.1Q VLAN) 
    Interface: 1 
Header type: ATPHRD_LOOP: Loopback 
Packet type: PACKET_MULTICAST 
    Address: 00 00 00 00 00 00 
     Data: ff ff ff ff ff ff 
       00 00 00 00 00 00 
       81 00 e0 02 
       08 00 
       5f 54 68 65 20 63 6f 6e 
       74 65 6e 74 73 20 6f 66 
       20 61 20 36 34 2d 62 79 
       74 65 20 45 74 68 65 72 
       6e 65 74 20 66 72 61 6d 
       65 5f 

第一個PACKET_OUTGOING被捕獲爲傳出;它顯示內核在發送數據包時沒有使用任何頭文件。

第二個PACKET_MULTICAST在它到達時被捕獲。 (由於以太網地址是FF:FF:FF:FF:FF:FF,因此它是一個多播數據包。)

正如您所見,後一個數據包只有802.1Q VLAN標頭 - 客戶端VLAN - - 內核已經使用了802.1AD服務VLAN標記。

以上證實至少是回送接口的場景。使用原始數據包接口,內核會使用802.1AD VLAN標頭(緊隨收件人地址的標頭)。如果您在接收器旁邊使用tcpdump -i eth0,您可以看到libpcap正在將消耗的標頭重新插入數據包!

Loopback接口有點特別,所以讓我們使用虛擬機重做測試。我恰好在運行最新的Xubuntu 14.04(截至2014-06-28安裝的所有更新,Ubuntu 3.13.0-29-generic#53 x86_64內核)。發送者HW地址是08 00 00 00 00 02,接收者是08 00 00 00 00 01,並且兩者都連接到內部網絡,沒有其他人在場。

(同樣,我在輸出中添加換行和縮進以使其更易於閱讀。)

發件人,虛擬機2上:

sudo ./sender eth0 08:00:00:00:00:01 '_The contents of a 64-byte Ethernet frame_' 

Sent 64 bytes: 08 00 00 00 00 01 
       08 00 00 00 00 02 
       88 a8 e0 01 
       81 00 e0 02 
       08 00 
       5f 54 68 65 20 63 6f 6e 
       74 65 6e 74 73 20 6f 66 
       20 61 20 36 34 2d 62 79 
       74 65 20 45 74 68 65 72 
       6e 65 74 20 66 72 61 6d 
       65 5f 

接收機,虛擬機1上:

sudo ./receiver eth0 

Received 60 bytes: 
    Protocol: ETH_P_8021Q (802.1Q VLAN) 
    Interface: 2 
Header type: ARPHRD_ETHER: Ethernet 10Mbps 
Packet type: PACKET_HOST 
    Address: 08 00 00 00 00 02 
     Data: 08 00 00 00 00 01 
       08 00 00 00 00 02 
       81 00 e0 02 
       08 00 
       5f 54 68 65 20 63 6f 6e 
       74 65 6e 74 73 20 6f 66 
       20 61 20 36 34 2d 62 79 
       74 65 20 45 74 68 65 72 
       6e 65 74 20 66 72 61 6d 
       65 5f 

正如你可以看到,結果是基本相同的回送的情況。特別是,802.1AD服務VLAN標記在接收時被消耗。 (您可以使用tcpdump或wireshark來比較接收的數據包:libpcap顯然是將消耗的VLAN標記包重新插入數據包中。)

如果您擁有最近足夠的內核(2013年4月添加了support),那麼您可以使用配置上的收件人802.1ad的VLAN(S):

sudo modprobe 8021q 

sudo ip link add link eth0 eth0.service1 type vlan proto 802.1ad id 1 

接收上eth0將收到的所有數據包,但eth0.service1只能用802.1ad的VLAN標籤的數據包,用VLAN ID 1.它確實捕獲具有相同VLAN ID的802.1Q VLAN標記的幀,這意味着您可以爲802.1AD和802完成全部接收路由。 1Q VLAN。

我不信任上面的測試,我自己;我創建了許多802.1AD和802.1Q VLAN,每個實例上都有獨立的receive實例,並更改了數據包標頭(不僅服務(第一)tci()和客戶端(第二個)tci()rawpacket_qinq()呼叫sender.c更改服務和客戶端VLAN ID,但也要更改rawpacket.h,以驗證802.1AD(88a8)和802.1Q(8100)VLAN標頭在接收時是否正確路由)。它一切都很好,沒有任何打嗝。

總結:

鑑於最近足夠的Linux內核版本中,以太網幀正確Linux內核(由8021q模塊)路由上接收,包括802.1ad的和802.1Q VLAN單獨的接口具有相同VLAN ID。即使沒有配置VLAN,內核也會消耗用於路由的VLAN標頭。

有問題?

+0

'編輯1:由於OP沒有麻煩評論下面的任何內容,我決定自己調查細節 - 我不瞭解你,但是我有一份非常繁忙的工作,需要我隨着時間的推移工作至少每週3或4天,加上試圖有社交生活的情侶,儘管你的帖子沒有太多時間閱讀,並且測試了你建議的所有內容,然後在2天后回覆。這不是一個種族傢伙:)我很感激幫助,我在最近兩個晚上看過這個問題,現在必須等待,因爲我週末不在了。「溫爾斯頓,這是我,威利! 「 – jwbensley

+1

@jwbensley:我只是誠實。我會爲你添加一個簡單的摘要,作爲對原始問題的評論,並編輯或刪除這個答案,這樣它就不會冒犯你的優先級和敏感性,* effendi *。 –

+1

一個奇妙的答案 - 我已經給它一個很好的閱讀,但我整個週末都不在,我很累。我會明天再讀一遍,試着做一些自我測試。再次感謝您的精彩回答!很高興我不是唯一有這個問題的人,很高興看到有人已經在案件中! :) – jwbensley