Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 54 additions & 5 deletions src/router/iface/bridge.d
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,14 @@ nothrow @nogc:
BaseInterface member_iface(size_t i)
=> _members[i].iface;

final override bool can_assist_ethernet_sources() const
{
foreach (ref member; _members)
if ((member.iface.caps & InterfaceCaps.ethernet) != 0 && member.iface.can_forward_ethernet_sources())
return true;
return false;
}

// Mark/unmark a member as kernel-offloaded (by identity, not removal -- the
// member stays in _members, keeping port indices and the address table stable).
void set_member_offloaded(BaseInterface iface, bool offloaded)
Expand Down Expand Up @@ -439,6 +447,7 @@ protected:
cb(entry.bridge_tag, MessageState.aborted);
recycle_tracking(entry);
}
_sta_assist_peers.clear();
return super.shutdown();
}

Expand Down Expand Up @@ -476,13 +485,22 @@ protected:
send(packet, _attach_port);
}

// Decapped exotic traffic enters the exotic switching domain at the attachment.
final override void station_deliver(ref Packet inner)
// Decapped OW traffic enters switching at the attachment.
final override void station_deliver(ref Packet inner, MACAddress station)
{
if (inner.type == PacketType.ethernet)
learn_assist_peer(station, inner.vlan);
ulong src_address = get_network_src_address(inner);
if (!src_address.is_multicast_address)
_address_table.insert(src_address, _attach_port);

ulong dst_address = get_network_dst_address(inner);
if (inner.type == PacketType.ethernet &&
(dst_address.is_multicast_address || _address_table.get(dst_address) < 0))
{
Packet local = inner;
local_dispatch(local);
}
send(inner, _attach_port);
}

Expand Down Expand Up @@ -679,11 +697,39 @@ private:
BridgePort _bridge_port;
Array!BridgePort _members;
AddressTable _address_table;
Array!STAAssistPeer _sta_assist_peers;

TagTracking* _tracking_free;
TagTracking* _tracking_active;
TagAllocator _bridge_tags;

struct STAAssistPeer
{
MACAddress station;
ushort vlan;
}

void learn_assist_peer(MACAddress station, ushort vlan)
{
ushort vid = vlan & 0x0FFF;
foreach (ref peer; _sta_assist_peers[])
if (peer.station == station && peer.vlan == vid)
return;
_sta_assist_peers ~= STAAssistPeer(station, vid);
}

bool send_assist_flood(ref Packet packet)
{
bool sent;
ushort vid = packet.vlan & 0x0FFF;
foreach (ref peer; _sta_assist_peers[])
{
if (peer.vlan == vid && station_egress(packet, peer.station))
sent = true;
}
return sent;
}

// an exotic address is ours if it lives behind a software-domain port (a local
// endpoint or an exotic member), not across the ethernet domain
bool software_domain_port(ubyte port)
Expand Down Expand Up @@ -745,8 +791,7 @@ private:
}
else if (dst_port == _attach_port)
{
// exotic packet crossing to the ethernet domain
if (!is_eth && !station_egress(packet))
if (!station_egress(packet))
add_tx_drop();
}
else if (dst_port == _cpu_port)
Expand Down Expand Up @@ -785,6 +830,8 @@ private:
}

// broadcast, or unknown destination: flood within the packet's switching domain
if (is_eth && !_sta_assist_peers.empty && src_port != _attach_port)
send_assist_flood(packet);
foreach (i, ref member; _members)
{
if (i == src_port || member.offloaded || !member.iface.running)
Expand Down Expand Up @@ -912,7 +959,7 @@ private:
{
// crossing to the ethernet domain is synchronous
recycle_tracking(tracking);
if (is_eth || !station_egress(packet))
if (!station_egress(packet))
return -1;
add_tx_frame(packet.data.length);
return 0;
Expand Down Expand Up @@ -963,6 +1010,8 @@ private:
}

// broadcast / unknown destination: flood within the packet's switching domain
if (is_eth && !_sta_assist_peers.empty && send_assist_flood(packet))
any_succeeded = true;
foreach (i, ref member; _members)
{
bool eth_member = (member.iface.caps & InterfaceCaps.ethernet) != 0;
Expand Down
53 changes: 38 additions & 15 deletions src/router/iface/ethernet.d
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import router.iface.endpoint : ether_neighbour_learn;

nothrow @nogc:

enum size_t ethernet_frame_capacity = 1537;

abstract class EthernetStation : BaseInterface
{
Expand Down Expand Up @@ -124,13 +125,21 @@ protected:
// Egress seam toward the segment; takes a fully-formed ethernet packet.
abstract void medium_tx(ref Packet packet);

// Where locally-decapped exotic traffic enters: standalone = local delivery;
// Bridge redirects this into its exotic switching domain.
void station_deliver(ref Packet inner)
// Where locally-decapped OW traffic enters.
void station_deliver(ref Packet inner, MACAddress)
{
dispatch(inner);
}

void station_control_received(OWControl, const(ubyte)[], MACAddress, ushort)
{
}

final void station_control_send(OWControl msg, MACAddress dst, scope const(ubyte)[] content, ushort vlan = 0)
{
station_send_control(msg, dst, content, vlan);
}

override int transmit(ref Packet packet, MessageCallback, const(QueuePolicy)*)
{
switch (packet.type)
Expand Down Expand Up @@ -243,8 +252,8 @@ protected:
discover(0);
}

// Encapsulate an exotic packet and transmit it across the segment.
final bool station_egress(ref const Packet packet)
// Encapsulate a packet and transmit it across the segment.
final bool station_egress(ref const Packet packet, MACAddress station = MACAddress())
{
const(PacketCodec)* codec = get_ow_codec(packet.type);
if (!codec)
Expand All @@ -258,15 +267,15 @@ protected:
if (!is_network_multicast_address(src))
_local_addresses[src] = getTime();

ubyte[1518] buffer = void;
ubyte[ethernet_frame_capacity] buffer = void;
ptrdiff_t len = build_ow_payload(packet, *codec, buffer);
if (len <= 0)
return false;

Packet wrapped;
ref eth = wrapped.init!Ethernet(buffer[0 .. len], packet.creation_time);
eth.src = mac;
eth.dst = resolve(get_network_dst_address(packet));
eth.dst = station ? station : resolve(get_network_dst_address(packet));
eth.ether_type = EtherType.ow;
wrapped.vlan = packet.vlan;
medium_tx(wrapped);
Expand All @@ -291,7 +300,8 @@ protected:
if (content.length < 5 + data_len)
return false;
add_rx_frame(packet.length);
station_control(cast(OWControl)wire_type, content[5 .. 5 + data_len], packet.eth.src, !packet.eth.dst.is_multicast);
station_control(cast(OWControl)wire_type, content[5 .. 5 + data_len], packet.eth.src,
!packet.eth.dst.is_multicast, packet.vlan);
return true;
}

Expand All @@ -317,7 +327,7 @@ protected:

// the inner packet accounts where it terminates; the encap overhead counts here
_status.rx_bytes += packet.length - inner.length;
station_deliver(inner);
station_deliver(inner, packet.eth.src);
return true;
}

Expand Down Expand Up @@ -386,7 +396,7 @@ protected:
storeBigEndian(ethertype++, ushort(EtherType.ow));
sink(hdr[0 .. cast(ubyte*)ethertype - hdr.ptr]);

ubyte[1518] buffer = void;
ubyte[ethernet_frame_capacity] buffer = void;
ptrdiff_t len = build_ow_payload(packet, *codec, buffer);
if (len <= 0)
return;
Expand Down Expand Up @@ -561,7 +571,7 @@ private:
static MACAddress cfm_class_multicast(ubyte level) pure
=> MACAddress(0x01, 0x80, 0xC2, 0x00, 0x00, cast(ubyte)(0x30 | level));

void station_send_control(OWControl msg, MACAddress dst, scope const(ubyte)[] content)
void station_send_control(OWControl msg, MACAddress dst, scope const(ubyte)[] content, ushort vlan = 0)
{
ubyte[512] buffer = void;
if (content.length + 5 > buffer.length)
Expand All @@ -576,10 +586,11 @@ private:
eth.src = mac;
eth.dst = dst;
eth.ether_type = EtherType.ow;
wrapped.vlan = vlan;
medium_tx(wrapped);
}

void station_control(OWControl msg, const(ubyte)[] content, MACAddress src, bool unicast)
void station_control(OWControl msg, const(ubyte)[] content, MACAddress src, bool unicast, ushort vlan)
{
switch (msg)
{
Expand Down Expand Up @@ -648,6 +659,15 @@ private:
_announce_sink(this, src, content);
return;

case OWControl.sta_assist_solicit:
if (content.length == 4 && can_assist_ethernet_sources())
station_send_control(OWControl.sta_assist_offer, src, content, vlan);
return;

case OWControl.sta_assist_offer:
station_control_received(msg, content, src, vlan);
return;

default:
return;
}
Expand Down Expand Up @@ -825,6 +845,11 @@ protected:

// the packet accounts only payload bytes; account the link-layer overhead here
_status.rx_bytes += data.length - packet.length;
medium_ingress(packet);
}

void medium_ingress(ref Packet packet)
{
incoming_packet(packet);
}

Expand All @@ -833,22 +858,20 @@ protected:
{
debug assert(packet.type == PacketType.ethernet, "medium_tx expects an ethernet packet");

ubyte[1518] buffer = void; // 1500 IP + 14 ETH + 4 VLAN. TODO: jumbos / double-tag.
ubyte[ethernet_frame_capacity] buffer = void;

Ethernet* eth = cast(Ethernet*)buffer.ptr;
eth.dst = packet.eth.dst;
eth.src = packet.eth.src;
ushort* ethertype = &eth.ether_type;

// if there should be a vlan header
if (packet.vlan)
{
storeBigEndian(ethertype++, ushort(EtherType.vlan));
storeBigEndian(ethertype++, packet.vlan);
}
storeBigEndian(ethertype++, packet.eth.ether_type);

// write the payload...
ubyte* payload = cast(ubyte*)ethertype;
if (packet.data.length > buffer.sizeof - (payload - buffer.ptr))
{
Expand Down
6 changes: 6 additions & 0 deletions src/router/iface/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,12 @@ nothrow @nogc:
final InterfaceCaps caps() const pure
=> _caps;

bool can_forward_ethernet_sources() const pure
=> true;

bool can_assist_ethernet_sources() const
=> false;

ushort pcap_type() const
=> 0;

Expand Down
51 changes: 50 additions & 1 deletion src/router/iface/packet.d
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ enum OWControl : ushort
addr_query = ow_control_flag | 0x0002, // body: [txid:u32 BE][PacketType:u16 BE, unknown = all] -- report your addresses
addr_report = ow_control_flag | 0x0003, // body: [txid:u32 BE, 0 = unsolicited][name_len:u8][name][N x universal address (u64 BE)]
announce = ow_control_flag | 0x0004, // body: identity TLVs (see manager.sync.discovery) -- peering beacon
sta_assist_solicit = ow_control_flag | 0x0005, // body: nonce (u32 BE)
sta_assist_offer = ow_control_flag | 0x0006, // body: echoed nonce (u32 BE)
}

// 802.1p PCP traffic classes
Expand Down Expand Up @@ -290,6 +292,31 @@ struct Ethernet
else
return ((address >> 40) & 1) != 0;
}

static ptrdiff_t encode_ow_header(ref const Packet p, ubyte[] buffer) nothrow @nogc
{
import urt.endian : nativeToBigEndian;
if (buffer.length < 14)
return -1;
ref const eth = p.hdr!Ethernet;
buffer[0 .. 6] = eth.dst.b[];
buffer[6 .. 12] = eth.src.b[];
buffer[12 .. 14] = eth.ether_type.nativeToBigEndian;
return 14;
}

static ptrdiff_t decode_ow_header(ref Packet p, const(ubyte)[] header) nothrow @nogc
{
import urt.endian : bigEndianToNative;
if (header.length < 14)
return -1;
p.type = PacketType.ethernet;
ref eth = p.hdr!Ethernet;
eth.dst = MACAddress(header[0 .. 6]);
eth.src = MACAddress(header[6 .. 12]);
eth.ether_type = header[12 .. 14].bigEndianToNative!ushort;
return 14;
}
}

struct Wifi80211
Expand Down Expand Up @@ -370,7 +397,9 @@ static assert(Wifi80211.sizeof == 24);

private:

__gshared PacketCodec[PacketType.count] g_packet_codecs = [ PacketCodec(), PacketCodec(&Ethernet.extract_src, &Ethernet.extract_dst, &Ethernet.is_multicast) ];
__gshared PacketCodec[PacketType.count] g_packet_codecs = [ PacketCodec(), PacketCodec(
&Ethernet.extract_src, &Ethernet.extract_dst, &Ethernet.is_multicast,
&Ethernet.encode_ow_header, &Ethernet.decode_ow_header) ];

ref const(PacketCodec) packet_codec(PacketType type) pure
{
Expand All @@ -379,3 +408,23 @@ ref const(PacketCodec) packet_codec(PacketType type) pure
alias FP = ref const(PacketCodec) function(PacketType) pure nothrow @nogc;
return (cast(FP)&impl)(type);
}


unittest
{
Packet original;
ref eth = original.init!Ethernet(null);
eth.dst = MACAddress(0x10, 0x20, 0x30, 0x40, 0x50, 0x60);
eth.src = MACAddress(0x02, 0x03, 0x04, 0x05, 0x06, 0x07);
eth.ether_type = EtherType.ip4;

ubyte[14] header;
assert(Ethernet.encode_ow_header(original, header) == header.length);

Packet decoded;
assert(Ethernet.decode_ow_header(decoded, header) == header.length);
assert(decoded.type == PacketType.ethernet);
assert(decoded.eth.dst == eth.dst);
assert(decoded.eth.src == eth.src);
assert(decoded.eth.ether_type == eth.ether_type);
}
12 changes: 12 additions & 0 deletions src/router/iface/vlan.d
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,18 @@ nothrow @nogc:
return _interface.msg_state(msg_handle);
}

override bool can_forward_ethernet_sources() const pure
{
const(BaseInterface) parent = _interface.get;
return parent && parent.can_forward_ethernet_sources();
}

override bool can_assist_ethernet_sources() const
{
const(BaseInterface) parent = _interface.get;
return parent && parent.can_assist_ethernet_sources();
}

protected:

override bool validate() const
Expand Down
Loading
Loading