Fixed MAC Address Conflict detection support.
VMS engineers designed the address conflict strategy when essentially all LANs were single collision domains (i.e. ALL nodes which might be affected by an address conflict were physically present on a single Ethernet cable which might have been extended by a couple of repeaters). Since that time, essentially no networks are single collision domains. Thick and thinwire Ethernet cables don’t exist and very few networks even have hubs. Today, essentially all LANs are deployed using one or more layers of network switches. In a switched LAN environment, the switches on the LAN ‘learn’ which ports on the LAN source traffic from which MAC addresses and then forward traffic destined for particular MAC address to the appropriate ports. If a particular MAC address is already in use somewhere on the LAN, then the switches ‘know’ where it is. The host based test using the loopback protocol is poorly designed to detect this condition. This test is performed by the host first changing the device’s Physical MAC address to the address which is to be tested, and then sending a loopback packet FROM AND TO this MAC address with a loopback reply to be sent by a system which may be currently using the MAC address. If no reply is received, then the MAC address is presumed to be unused. The sending of this packet will result in its delivery to the right system since the switch port/MAC address tables know where to deliver packets destined to this MAC address, however the response it generates won’t be delivered to the system performing the test since the switches on the LAN won’t know about the local port being the right target for packets with this MAC address. A better test design to detect these conflicts would be for the testing system to send a loopback packet FROM the current physical MAC address (BEFORE changing it) TO the MAC address being tested with the loopback response coming to the current physical MAC address of the device. If a response is received, then the address is in use and the attempt to change the device’s MAC address should fail. Since we can’t change the software running in these simulators to implement this better conflict detection approach, we can still ‘do the right thing’ in the sim_ether layer. We’re already handling the loopback test packets specially since we always had to avoid receiving the packets which were being sent, but needed to allow for the incoming loopback packets to be properly dealt with. We can extend this current special handling to change outgoing ‘loopback to self’ packets to have source AND loopback destination addresses in the packets to be the host NIC’s physical address. The switch network will already know the correct MAC/port relationship for the host NIC’s physical address, so loopback response packets will be delivered as needed.
This commit is contained in:
parent
4ab52659be
commit
4f6ad32395
2 changed files with 115 additions and 14 deletions
100
sim_ether.c
100
sim_ether.c
|
@ -1721,6 +1721,66 @@ char mac_string[32];
|
|||
eth_mac_fmt(mac, mac_string);
|
||||
sim_debug(dev->dbit, dev->dptr, "Determining Address Conflict for MAC address: %s\n", mac_string);
|
||||
|
||||
/* The process of checking address conflicts is used in two ways:
|
||||
1) to determine the behavior of the currently running packet
|
||||
delivery facility regarding whether it may receive copies
|
||||
of every packet sent (and how many).
|
||||
2) to verify if a MAC address which this facility is planning
|
||||
to use as the source address of packets is already in use
|
||||
by some other node on the local network
|
||||
Case #1, doesn't require (and explicitly doesn't want) any
|
||||
interaction or response from other systems on the LAN so
|
||||
therefore no considerations regarding switch packet forwarding
|
||||
are important. Meanwhile, Case #2 does require responses from
|
||||
other components on the LAN to provide useful functionality.
|
||||
The original designers of this mechanism did this when essentially
|
||||
all LANs were single collision domains (i.e. ALL nodes which might
|
||||
be affected by an address conflict were physically present on a single
|
||||
Ethernet cable which might have been extended by a couple of repeaters).
|
||||
Since that time, essentially no networks are single collision domains.
|
||||
Thick and thinwire Ethernet cables don’t exist and very few networks
|
||||
even have hubs. Today, essentially all LANs are deployed using one
|
||||
or more layers of network switches. In a switched LAN environment, the
|
||||
switches on the LAN ‘learn’ which ports on the LAN source traffic from
|
||||
which MAC addresses and then forward traffic destined for particular
|
||||
MAC address to the appropriate ports. If a particular MAC address is
|
||||
already in use somewhere on the LAN, then the switches ‘know’ where
|
||||
it is. The host based test using the loopback protocol is poorly
|
||||
designed to detect this condition. This test is performed by the host
|
||||
first changing the device’s Physical MAC address to the address which
|
||||
is to be tested, and then sending a loopback packet FROM AND TO this
|
||||
MAC address with a loopback reply to be sent by a system which may be
|
||||
currently using the MAC address. If no reply is received, then the
|
||||
MAC address is presumed to be unused. The sending of this packet will
|
||||
result in its delivery to the right system since the switch port/MAC
|
||||
address tables know where to deliver packets destined to this MAC
|
||||
address, however the response it generates won’t be delivered to the
|
||||
system performing the test since the switches on the LAN won’t know
|
||||
about the local port being the right target for packets with this MAC
|
||||
address. A better test design to detect these conflicts would be for
|
||||
the testing system to send a loopback packet FROM the current physical
|
||||
MAC address (BEFORE changing it) TO the MAC address being tested with
|
||||
the loopback response coming to the current physical MAC address of
|
||||
the device. If a response is received, then the address is in use and
|
||||
the attempt to change the device’s MAC address should fail. Since we
|
||||
can’t change the software running in these simulators to implement this
|
||||
better conflict detection approach, we can still ‘do the right thing’
|
||||
in the sim_ether layer. We’re already handling the loopback test
|
||||
packets specially since we always had to avoid receiving the packets
|
||||
which were being sent, but needed to allow for the incoming loopback
|
||||
packets to be properly dealt with. We can extend this current special
|
||||
handling to change outgoing ‘loopback to self’ packets to have source
|
||||
AND loopback destination addresses in the packets to be the host NIC’s
|
||||
physical address. The switch network will already know the correct
|
||||
MAC/port relationship for the host NIC’s physical address, so loopback
|
||||
response packets will be delivered as needed.
|
||||
|
||||
Code in _eth_write and _eth_callback provide the special handling to
|
||||
perform the described loopback packet adjustments, and code in
|
||||
eth_filter_hash makes sure that the loopback response packets are received.
|
||||
|
||||
*/
|
||||
|
||||
/* build a loopback forward request packet */
|
||||
memset (&send, 0, sizeof(ETH_PACK));
|
||||
send.len = ETH_MIN_PACKET; /* minimum packet size */
|
||||
|
@ -1740,7 +1800,7 @@ if (status != SCPE_OK) {
|
|||
char *msg;
|
||||
msg = "Eth: Error Transmitting packet: %s\r\n"
|
||||
"You may need to run as root, or install a libpcap version\r\n"
|
||||
"which is at least 0.9 from www.tcpdump.org\r\n";
|
||||
"which is at least 0.9 from your OS vendor or www.tcpdump.org\r\n";
|
||||
printf(msg, strerror(errno));
|
||||
if (sim_log) fprintf (sim_log, msg, strerror(errno));
|
||||
return status;
|
||||
|
@ -1752,7 +1812,10 @@ sim_os_ms_sleep (300); /* time for a conflicting host to respond */
|
|||
do {
|
||||
memset (&recv, 0, sizeof(ETH_PACK));
|
||||
status = eth_read (dev, &recv, NULL);
|
||||
if (memcmp(send.msg, recv.msg, 14)== 0)
|
||||
if (((0 == memcmp(send.msg+12, recv.msg+12, 2)) && /* Protocol Match */
|
||||
(0 == memcmp(send.msg, recv.msg+6, 6)) && /* Source Match */
|
||||
(0 == memcmp(send.msg+6, recv.msg, 6))) || /* Destination Match */
|
||||
(0 == memcmp(send.msg, recv.msg, 14))) /* Packet Match (Reflection) */
|
||||
responses++;
|
||||
} while (recv.len > 0);
|
||||
|
||||
|
@ -1789,12 +1852,16 @@ if (!packet) return SCPE_ARG;
|
|||
|
||||
/* make sure packet is acceptable length */
|
||||
if ((packet->len >= ETH_MIN_PACKET) && (packet->len <= ETH_MAX_PACKET)) {
|
||||
int loopback_self_frame = LOOPBACK_SELF_FRAME(dev->physical_addr, packet->msg);
|
||||
int loopback_self_frame = LOOPBACK_SELF_FRAME(packet->msg, packet->msg);
|
||||
|
||||
eth_packet_trace (dev, packet->msg, packet->len, "writing");
|
||||
|
||||
/* record sending of loopback packet (done before actual send to avoid race conditions with receiver) */
|
||||
if (loopback_self_frame) {
|
||||
if (dev->have_host_nic_phy_addr) {
|
||||
memcpy(&packet->msg[6], dev->host_nic_phy_hw_addr, sizeof(ETH_MAC));
|
||||
memcpy(&packet->msg[18], dev->host_nic_phy_hw_addr, sizeof(ETH_MAC));
|
||||
}
|
||||
#ifdef USE_READER_THREAD
|
||||
pthread_mutex_lock (&dev->self_lock);
|
||||
#endif
|
||||
|
@ -2343,6 +2410,17 @@ int from_me = 0;
|
|||
int i;
|
||||
int bpf_used;
|
||||
|
||||
if ((dev->have_host_nic_phy_addr) &&
|
||||
(LOOPBACK_PHYSICAL_RESPONSE(dev->host_nic_phy_hw_addr, dev->physical_addr, data))) {
|
||||
u_char *datacopy = malloc(header->len);
|
||||
|
||||
memcpy(datacopy, data, header->len);
|
||||
memcpy(datacopy, dev->physical_addr, sizeof(ETH_MAC));
|
||||
memcpy(datacopy+18, dev->physical_addr, sizeof(ETH_MAC));
|
||||
_eth_callback(info, header, datacopy);
|
||||
free(datacopy);
|
||||
return;
|
||||
}
|
||||
switch (dev->eth_api) {
|
||||
case ETH_API_PCAP:
|
||||
#ifdef USE_BPF
|
||||
|
@ -2377,7 +2455,9 @@ switch (dev->eth_api) {
|
|||
}
|
||||
|
||||
/* detect reception of loopback packet to our physical address */
|
||||
if (LOOPBACK_SELF_FRAME(dev->physical_addr, data)) {
|
||||
if ((LOOPBACK_SELF_FRAME(dev->physical_addr, data)) ||
|
||||
(dev->have_host_nic_phy_addr &&
|
||||
LOOPBACK_PHYSICAL_REFLECTION(dev->host_nic_phy_hw_addr, data))) {
|
||||
#ifdef USE_READER_THREAD
|
||||
pthread_mutex_lock (&dev->self_lock);
|
||||
#endif
|
||||
|
@ -2394,7 +2474,7 @@ if (LOOPBACK_SELF_FRAME(dev->physical_addr, data)) {
|
|||
#ifdef USE_READER_THREAD
|
||||
pthread_mutex_unlock (&dev->self_lock);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (bpf_used ? to_me : (to_me && !from_me)) {
|
||||
if (header->len > ETH_MIN_JUMBO_FRAME) {
|
||||
|
@ -2686,6 +2766,10 @@ if ((addr_count) && (dev->reflections > 0)) {
|
|||
memcpy(dev->physical_addr, &dev->filter_address[i], sizeof(ETH_MAC));
|
||||
/* let packets through where dst and src are the same as our physical address */
|
||||
sprintf (&buf[strlen(buf)], " or ((ether dst %s) and (ether src %s))", mac, mac);
|
||||
if (dev->have_host_nic_phy_addr) {
|
||||
eth_mac_fmt(&dev->host_nic_phy_hw_addr, mac);
|
||||
sprintf(&buf[strlen(buf)], "or ((ether dst %s) and (ether proto 0x9000))", mac);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -2694,9 +2778,9 @@ if ((0 == strlen(buf)) && (!dev->promiscuous)) /* Empty filter means match nothi
|
|||
strcpy(buf, "ether host fe:ff:ff:ff:ff:ff"); /* this should be a good match nothing filter */
|
||||
sim_debug(dev->dbit, dev->dptr, "BPF string is: |%s|\n", buf);
|
||||
|
||||
/* get netmask, which is a required argument for compiling, the value, in our
|
||||
case isn't actually interesting since the filters we generate aren't
|
||||
referencing IP fields, networks or values */
|
||||
/* get netmask, which is a required argument for compiling. The value,
|
||||
in our case isn't actually interesting since the filters we generate
|
||||
aren't referencing IP fields, networks or values */
|
||||
if ((dev->eth_api == ETH_API_PCAP) && (pcap_lookupnet(dev->name, &bpf_subnet, &bpf_netmask, errbuf)<0))
|
||||
bpf_netmask = 0;
|
||||
|
||||
|
|
27
sim_ether.h
27
sim_ether.h
|
@ -136,13 +136,30 @@
|
|||
#define ETH_MIN_JUMBO_FRAME ETH_MAX_PACKET /* Threshold size for Jumbo Frame Processing */
|
||||
|
||||
#define LOOPBACK_SELF_FRAME(phy_mac, msg) \
|
||||
((memcmp(phy_mac, msg , 6) == 0) && \
|
||||
(memcmp(phy_mac, msg+6, 6) == 0) && \
|
||||
((msg)[12] == 0x90) && ((msg)[13] == 0x00) && \
|
||||
(((msg)[12] == 0x90) && ((msg)[13] == 0x00) && \
|
||||
((msg)[14] == 0x00) && ((msg)[15] == 0x00) && \
|
||||
((msg)[16] == 0x02) && ((msg)[17] == 0x00) && \
|
||||
(memcmp(phy_mac, msg+18, 6) == 0) && \
|
||||
((msg)[24] == 0x01) && ((msg)[25] == 0x00))
|
||||
((msg)[24] == 0x01) && ((msg)[25] == 0x00) && \
|
||||
(memcmp(phy_mac, (msg), 6) == 0) && \
|
||||
(memcmp(phy_mac, (msg)+6, 6) == 0) && \
|
||||
(memcmp(phy_mac, (msg)+18, 6) == 0))
|
||||
|
||||
#define LOOPBACK_PHYSICAL_RESPONSE(host_phy, phy_mac, msg) \
|
||||
(((msg)[12] == 0x90) && ((msg)[13] == 0x00) && \
|
||||
((msg)[14] == 0x08) && ((msg)[15] == 0x00) && \
|
||||
((msg)[16] == 0x02) && ((msg)[17] == 0x00) && \
|
||||
((msg)[24] == 0x01) && ((msg)[25] == 0x00) && \
|
||||
(memcmp(host_phy, (msg)+18, 6) == 0) && \
|
||||
(memcmp(host_phy, (msg), 6) == 0) && \
|
||||
(memcmp(phy_mac, (msg)+6, 6) == 0))
|
||||
|
||||
#define LOOPBACK_PHYSICAL_REFLECTION(host_phy, msg) \
|
||||
(((msg)[12] == 0x90) && ((msg)[13] == 0x00) && \
|
||||
((msg)[14] == 0x00) && ((msg)[15] == 0x00) && \
|
||||
((msg)[16] == 0x02) && ((msg)[17] == 0x00) && \
|
||||
((msg)[24] == 0x01) && ((msg)[25] == 0x00) && \
|
||||
(memcmp(host_phy, (msg)+6, 6) == 0) && \
|
||||
(memcmp(host_phy, (msg)+18, 6) == 0))
|
||||
|
||||
struct eth_packet {
|
||||
uint8 msg[ETH_FRAME_SIZE]; /* ethernet frame (message) */
|
||||
|
|
Loading…
Add table
Reference in a new issue