2014-06-22 6 views
7

मैंने एक सी प्रोग्राम लिखा है जो ईथरनेट फ्रेम सीधे तार पर लिख रहा है (जो दो मोड, प्रेषक या रिसीवर में चलता है)। प्रेषक उन पर दो वीएलएएन टैग के साथ फ्रेम भेज रहा है (QinQ) लेकिन अजीब बात यह है कि जब फ्रेम रिसीवर तक पहुंचता है तो ईथरटाइप मानक (सिंगल) वीएलएएन encapsulated फ्रेम के रूप में बदल गया है। क्या यह संभव है कि एनआईसी ऐसा कर रहा है, या लिनक्स इसे अनुमति नहीं देता है? Wireshark tcpdump के समान व्यवहार दिखाता है।लिनक्स: ईथरनेट फ्रेम भेजते समय ईथरटाइप को फिर से लिखा जा रहा है

प्रेषक के नीचे की छवि को समझाने के लिए प्रेषक ईथरनेट प्रसारण पते पर फ्रेम भेज रहा है एफएफ: एफएफ: एफएफ: एफएफ: एफएफ: एफएफ रिसीवर खोजने के लिए (ये दो परीक्षण मशीनें हैं जो एक क्रॉसओवर केबल के माध्यम से जुड़ी हैं लेकिन नीचे का परिणाम है एक स्विच या हब के साथ ही)। जैसा कि आप देख सकते हैं कि फ्रेम उन पर दो वीएलएएन टैग के साथ आ रहे हैं, बाहरी टैग में 0x8100 का ईथरटाइप और 40 का वीएलएएन आईडी है, आंतरिक वीएलएएन में ईथरटाइप 0x8100 और 20 की वीएलएएन आईडी है। जैसा कि हम सभी जानते हैं, QinQ फ्रेम के साथ बाहरी फ्रेम 0x88a8 का एक ethertype होना चाहिए!

जब मेरे आवेदन में प्रेषक से फ्रेम भेजे जाते हैं तो उनके पास 0x88a8 का बाहरी ईथरटाइप होता है लेकिन नीचे दी गई तस्वीर के अनुसार उन्हें आंतरिक और बाहरी ईथर दोनों पर 0x8100 के साथ प्राप्त किया जाता है। हाइलाइट किया गया टेक्स्ट रिसीवर एक जवाब भेज रहा है, क्योंकि आप देख सकते हैं कि फ्रेम में बाहरी फ्रेम पर 0x88a8 और आंतरिक पर 0x8100 है। अन्य मशीन पर टीसीपीडम्प एक ही दिखाता है (यह वही कोड है! फ्रेम्स 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' का उपयोग करें कि आपके एनआईसी (ओं) पर कोई भी वीएलएएन ऑफलोडिंग अक्षम है। इसके अलावा, आप 'sockFD' कैसे खोल रहे हैं? –

+0

'sockFD = सॉकेट (AF_PACKET, SOCK_RAW, htons (ETH_P_ALL));' – jwbensley

+0

मैं प्राप्त करने और अपडेट करने के अगले मौके पर ethtool आउटपुट की जांच करूंगा। – jwbensley

उत्तर

11

(पूरी तरह से इस सवाल का जवाब आसान बनाने के लिए फिर से लिखा। मैं अपने सी शीर्षक और स्रोत फ़ाइलों में काफी कुछ बग ठीक नीचे सूचीबद्ध।)

linux-netdev mailing list पर एक चर्चा about exactly this था अप्रैल 2014 में, विषय "802.1AD पैकेट्स - कर्नेल सभी पैकेट पर 88 ए 8 से 8100 तक ईथर प्रकार बदलता है"

यह पता चला है कि कर्नेल ईथर प्रकार को नहीं बदलता है, यह बस इसे एक पैकेट प्राप्त करने पर खपत करता है। मैं नीचे दिखाता हूं कि हाल ही में पर्याप्त कर्नेल दिए गए वीएलएएन रूटिंग (802.1AD और 802.1Q वीएलएएन के लिए अलग-अलग नियमों सहित) के लिए इसका सही ढंग से उपयोग किया जाता है। भले ही वीएलएएन टैग का उपयोग रूटिंग के लिए नहीं किया जाता है (कहें, यदि कोई वीएलएएन कॉन्फ़िगर नहीं किया गया है, या यदि 8021q कर्नेल मॉड्यूल लोड नहीं होता है), तो VLAN टैग कर्नेल द्वारा खपत किया जाता है।

इस प्रकार, मूल प्रश्न, "इथर-प्रकार फिर से लिखा जा रहा है ईथरनेट भेजने फ्रेम", गलत है: इथर-प्रकार नहीं किया जा रहा फिर से लिखा है। यह कर्नेल द्वारा खपत है।

क्योंकि वीएलएएन टैग कर्नेल द्वारा उपभोग किया जाता है, libpcap - जो टीसीपीडम्प, वायर्सहार्क एट अल द्वारा उपयोग की जाने वाली पैकेट कैप्चर लाइब्रेरी है। - इसे वापस पैकेट हेडर में पुन: पेश करने का प्रयास करता है। दुर्भाग्यवश, यह हमेशा 802.1Q VLAN शीर्षलेख (8100) का उपयोग करता है।

libpcap में suggested change है जो libpcap में ठीक से इस समस्या को हल करता है, लेकिन इस लेखन के रूप में, ऐसा लगता है कि यह अभी तक शामिल नहीं है; आप अभी भी htons(ETH_P_8021Q)libpcap source file for Linux में कई स्थानों पर हार्डकोडेड देख सकते हैं।


मैं कल्पना नहीं कर सकते तो आप इस के लिए मेरे शब्द ले लेंगे, तो मुझे बताएंगे कि कैसे आप खुद के लिए यह पता लगाने कर सकते हैं।

चलिए एक साधारण पैकेट प्रेषक और रिसीवर लिखते हैं, जो कि 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 + सी) या इसे 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 

रिसीवर टर्मिनल में, कैसे 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, जैसा कि यह पहुंचा था, पर कब्जा कर लिया गया था। (चूंकि ईथरनेट पता एफएफ था: एफएफ: एफएफ: एफएफ: एफएफ: एफएफ, यह एक मल्टीकास्ट पैकेट है।)

जैसा कि आप देख सकते हैं, बाद वाले पैकेट में केवल 802.1Q वीएलएएन हैडर - क्लाइंट वीएलएएन - -, कर्नेल ने 802.1AD सेवा वीएलएएन टैग का उपभोग किया है।

उपरोक्त लूपबैक इंटरफ़ेस के लिए परिदृश्य की पुष्टि करता है, कम से कम। कच्चे पैकेट इंटरफेस का उपयोग करते हुए, कर्नेल 802.1AD वीएलएएन हेडर (प्राप्तकर्ता के पते के तुरंत बाद) का उपभोग करता है। यदि आप रिसीवर के साथ tcpdump -i eth0 का उपयोग करते हैं, तो आप देख सकते हैं कि libpcap उपभोग वाले शीर्षलेख को पैकेट पर वापस जोड़ रहा है!

लूपबैक इंटरफ़ेस थोड़ा सा विशेष है, तो आभासी मशीनों का उपयोग करके परीक्षण को फिर से करें। मैं अद्यतित जुबंटू 14.04 (2014-06-28 के रूप में स्थापित सभी अद्यतन, उबंटू 3.13.0-29-जेनेरिक # 53 x86_64 कर्नेल) चल रहा है। प्रेषक एचडब्ल्यू पता 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 सेवा वीएलएएन टैग का उपभोग किया गया था। (आप प्राप्त पैकेट की तुलना करने के लिए टीसीपीडम्प या वायरशर्क का उपयोग कर सकते हैं: libpcap स्पष्ट रूप से उपभोग वाले वीएलएएन टैग पैक को पैकेट में पुन: सम्मिलित कर रहा है।)

यदि आपके पास हाल ही में पर्याप्त कर्नेल (support अप्रैल 2013 में जोड़ा गया था), तो आप कर सकते हैं का उपयोग कर प्राप्तकर्ता पर एक 802.1AD VLAN (रों) कॉन्फ़िगर करें:

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 आईडी के साथ पर 1. यह नहीं है उसी वीएलएएन आईडी के साथ 802.1Q वीएलएएन टैग के साथ फ़्रेम कैप्चर करें, जिसका अर्थ है कि आप 802.1AD और 802 दोनों के लिए प्राप्त होने पर पूर्ण रूटिंग कर सकते हैं। 1 क्यू वीएलएएनएस

मुझे केवल उपर्युक्त परीक्षण पर भरोसा नहीं था; (नहीं मैं हर एक पर अलग receive उदाहरणों के साथ 802.1AD और 802.1Q VLANs का गठन किया है, और बदल पैकेट हेडर केवल सेवा (प्रथम) tci() और ग्राहक (दूसरा) rawpacket_qinq()में कॉल sender.c में tci() को 802.1AD (88a8) और 802.1Q (8100) VLAN शीर्षलेखों को प्राप्त करने के लिए सही ढंग से रूट किया गया है, यह सत्यापित करने के लिए सेवा और क्लाइंट वीएलएएन आईडी बदलें, लेकिन rawpacket.h को भी बदलना। यह सब बिना किसी हिचकी के खूबसूरती से काम किया।

सारांश में:

हाल ही में एक पर्याप्त लिनक्स कर्नेल संस्करण, ईथरनेट फ्रेम सही ढंग से, लिनक्स कर्नेल (8021q मॉड्यूल द्वारा) द्वारा रूट किया जाता है पर प्राप्त एक ही साथ 802.1AD और 802.1Q के लिए अलग VLAN इंटरफेस सहित देखते हुए वीएलएएन आईडी कर्नेल रूटिंग के लिए उपयोग किए जाने वाले वीएलएएन हेडर का उपभोग करता है, भले ही कोई वीएलएएन कॉन्फ़िगर नहीं किया गया हो।

प्रश्न?

+0

'संपादित करें 1: चूंकि ओपी ने नीचे दिए गए किसी भी टिप्पणी पर परेशान नहीं किया है, इसलिए मैंने अपने विवरणों की जांच करने का फैसला किया है - मुझे आपके बारे में पता नहीं है लेकिन मेरे पास बहुत व्यस्त नौकरी है जिसके लिए मुझे कम से कम समय के साथ काम करने की आवश्यकता है सप्ताह में 3 या 4 दिन, जोड़ी एक सामाजिक जीवन की कोशिश करने के साथ, जो आपकी पोस्ट के बाद पढ़ने के लिए ज्यादा समय नहीं छोड़ती है और आपके द्वारा सुझाए गए सब कुछ का परीक्षण करती है और फिर 2 दिन बाद प्रतिक्रिया देती है? यह रेस दोस्त नहीं है :) मैं मदद की सराहना करता हूं, मैंने पिछले दो रातों को इस मुद्दे पर देखा है, अब इसे इंतजार करना है क्योंकि मैं सप्ताहांत में दूर हूं ..... "चिल विंस्टन, यह मैं हूं, विली! " – jwbensley

+1

@jwbensley: मैं सिर्फ ईमानदार था। मैं आपके मूल प्रश्न पर टिप्पणी के रूप में आपके लिए एक सरल सारांश जोड़ूंगा और इस उत्तर को संपादित या हटा दूंगा, ताकि यह आपकी प्राथमिकताओं और संवेदनशीलताओं को अपमानित न करे, * effendi *। –

+1

एक शानदार जवाब - मैंने इसे एक अच्छा पढ़ा है लेकिन मैं सभी सप्ताहांत दूर गया हूं और मैं थक गया हूं। मैं कल इसे फिर से पढ़ूंगा और कोशिश करूँगा और कुछ परीक्षण करूँगा। शानदार जवाब के लिए फिर से धन्यवाद! खुशी है कि मैं इस समस्या वाले एकमात्र व्यक्ति नहीं हूं और यह देखना अच्छा लगता है कि कोई इस मामले पर पहले से ही है! :) – jwbensley

संबंधित मुद्दे