Skip to content

Instantly share code, notes, and snippets.

@zeroFruit
Created October 31, 2020 12:13
Show Gist options
  • Save zeroFruit/2277640e0442430eb20e9a3dbf465384 to your computer and use it in GitHub Desktop.
Save zeroFruit/2277640e0442430eb20e9a3dbf465384 to your computer and use it in GitHub Desktop.
Modeling the Internet from the scratch: Link-layer, LAN, Switch - Code snippet
package link
type Addr string
package na
// Card is abstracted network adapter part to simulate bytes transport on
// physical cable. Node's interface uses this interface to send frame
// between nodes.
type Card interface {
Send(buf []byte, addr string) (time.Time, error)
Recv() <-chan *FrameData
}
package link
type Link struct {
cost uint
ep1 EndPoint
ep2 EndPoint
}
func (l *Link) AttachEndpoint(ep EndPoint) error {
...
}
// Opposite returns other endpoint of given id. If other endpoint does not exist,
// then return error
func (l *Link) Opposite(id Id) (EndPoint, error) {
...
}
// EndPoint represents point of link. Link is the channel to pass data to end-point
// either side to the opposite
type EndPoint interface {
Id() Id
GetLink() *Link
AttachLink(link *Link) error
}
package link
type Transmitter interface {
Transmit(frame []byte) error
}
type Interface interface {
Transmitter
EndPoint
Address() types.HwAddr
}
// NetHandler receives serialized frame payload. With this, doing some high-level protocol
type NetHandler interface {
Handle(pl []byte)
}
type Host struct {
Interface Interface
netHandler NetHandler
frmEnc *FrameEncoder
frmDec *FrameDecoder
}
func Build() (host1 *link.Host, host2 *link.Host, host3 *link.Host,
swch1 *link.Switch, swch2 *link.Switch) {
// setup node
host1 = link.NewHost()
host2 = link.NewHost()
host3 = link.NewHost()
swch1 = link.NewSwitch()
swch2 = link.NewSwitch()
}
func Build() (host1 *link.Host, host2 *link.Host, host3 *link.Host,
swch1 *link.Switch, swch2 *link.Switch) {
// setup node
...
// setup interface
intf1 := link.NewInterface(40001, link.AddrFromStr("11-11-11-11-11-11"), host1)
attachInterface(host1, intf1)
intf2 := link.NewInterface(40002, link.AddrFromStr("22-22-22-22-22-22"), host2)
attachInterface(host2, intf2)
intf3 := link.NewInterface(40003, link.AddrFromStr("33-33-33-33-33-33"), host3)
attachInterface(host3, intf3)
sp11 := link.NewSwitchPort(40004, swch1)
sp12 := link.NewSwitchPort(40005, swch1)
sp13 := link.NewSwitchPort(40006, swch1)
attachSwchInterface(swch1, sp11, "1")
attachSwchInterface(swch1, sp12, "2")
attachSwchInterface(swch1, sp13, "3")
sp21 := link.NewSwitchPort(40007, swch2)
sp22 := link.NewSwitchPort(40008, swch2)
attachSwchInterface(swch2, sp21, "1")
attachSwchInterface(swch2, sp22, "2")
}
func Build() (host1 *link.Host, host2 *link.Host, host3 *link.Host,
swch1 *link.Switch, swch2 *link.Switch) {
// setup node
// setup interface
...
// setup link
link1 := link.NewLink(1)
attachLink(intf1, link1)
attachLink(sp11, link1)
link2 := link.NewLink(1)
attachLink(intf2, link2)
attachLink(sp12, link2)
link3 := link.NewLink(1)
attachLink(sp13, link3)
attachLink(sp21, link3)
link4 := link.NewLink(1)
attachLink(sp22, link4)
attachLink(intf3, link4)
return
}
// Port can transmit data and can be point of link. But it has no hardware
// address. Before using Port, it must register its own Id
type Port interface {
Transmitter
EndPoint
Register(id Id)
Registered() bool
}
type ForwardEntry struct {
// Incoming is port id attached to switch
Incoming Id
// Addr is destination node address
Addr types.HwAddr
// Time is timestamp when this entry is created
Time time.Time
}
type FrameForwardTable struct {
entries []ForwardEntry
}
type Switch struct {
PortList map[Id]Port
Table *FrameForwardTable
frmDec *FrameDecoder
frmEnc *FrameEncoder
}
// Forward receives id of port it receives frame, address of sender
// and frame to send to receiver. Based on id and address it determines whether to
// broadcast frame or forward it to others, otherwise just discard frame.
func (s *Switch) Forward(incoming Id, frame na.Frame) error {
s.Table.Update(incoming, frame.Src)
frm, err := s.frmEnc.Encode(frame)
if err != nil {
return err
}
entry, ok := s.Table.LookupByAddr(frame.Dest)
if !ok {
return s.broadcastExcept(incoming, frm)
}
if entry.Incoming.Equal(incoming) {
log.Printf("discard frame from id: %s, src: %s, dest: %s\n", incoming, frame.Src, frame.Dest)
return nil
}
p := s.PortList[entry.Incoming]
if err := p.Transmit(frm); err != nil {
return err
}
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment