mirror of
https://github.com/soypat/lneto.git
synced 2026-08-25 08:59:05 +00:00
arp working over network
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
package ipv4
|
||||
|
||||
const (
|
||||
sizeHeader = 20
|
||||
)
|
||||
|
||||
// ToS represents the Traffic Class (a.k.a Type of Service).
|
||||
type ToS uint8
|
||||
|
||||
// DS returns the top 6 bits of the IPv4 ToS holding the Differentiated Services field
|
||||
// which is used to classify packets.
|
||||
func (tos ToS) DS() uint8 { return uint8(tos) >> 2 }
|
||||
|
||||
// ECN is the Explicit Congestion Notification which provides congestion control and non-congestion control traffic.
|
||||
func (tos ToS) ECN() uint8 { return uint8(tos & 0b11) }
|
||||
|
||||
// Flags holds fragmentation field data of an IPv4 header.
|
||||
type Flags uint16
|
||||
|
||||
// IsEvil returns true if evil bit set as per [RFC3514].
|
||||
//
|
||||
// [RFC3514]: https://datatracker.ietf.org/doc/html/rfc3514
|
||||
func (f Flags) IsEvil() bool { return f&2000 != 0 }
|
||||
|
||||
// DontFragment specifies whether the datagram can not be fragmented.
|
||||
// This can be used when sending packets to a host that does not have resources to perform reassembly of fragments.
|
||||
// If the DontFragment(DF) flag is set, and fragmentation is required to route the packet, then the packet is dropped.
|
||||
func (f Flags) DontFragment() bool { return f&0x4000 != 0 }
|
||||
|
||||
// MoreFragments is cleared for unfragmented packets.
|
||||
// For fragmented packets, all fragments except the last have the MF flag set.
|
||||
// The last fragment has a non-zero Fragment Offset field, so it can still be differentiated from an unfragmented packet.
|
||||
func (f Flags) MoreFragments() bool { return f&0x8000 != 0 }
|
||||
|
||||
// FragmentOffset specifies the offset of a particular fragment relative to the beginning of the original unfragmented IP datagram.
|
||||
// Fragments are specified in units of 8 bytes, which is why fragment lengths are always a multiple of 8; except the last, which may be smaller.
|
||||
// The fragmentation offset value for the first fragment is always 0.
|
||||
func (f Flags) FragmentOffset() uint16 { return uint16(f) & 0x1fff }
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
package ipv4
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/soypat/lneto/lneto2"
|
||||
)
|
||||
|
||||
// Frame encapsulates the raw data of an IPv4 packet
|
||||
// and provides methods for manipulating, validating and
|
||||
// retreiving fields and payload data. See [RFC791].
|
||||
//
|
||||
// [RFC791]: https://tools.ietf.org/html/rfc791
|
||||
type Frame struct {
|
||||
buf []byte
|
||||
}
|
||||
|
||||
// RawData returns the underlying slice with which the frame was created.
|
||||
func (ifrm Frame) RawData() []byte { return ifrm.buf }
|
||||
|
||||
// HeaderLength returns the length of the IPv4 header as calculated using IHL. It includes IP options.
|
||||
func (ifrm Frame) HeaderLength() int {
|
||||
return int(ifrm.ihl()) * 4
|
||||
}
|
||||
|
||||
func (ifrm Frame) ihl() uint8 { return ifrm.buf[0] & 0xf }
|
||||
func (ifrm Frame) version() uint8 { return ifrm.buf[0] >> 4 }
|
||||
|
||||
// VersionAndIHL returns the version and IHL fields in the IPv4 header. Version should always be 4.
|
||||
func (ifrm Frame) VersionAndIHL() (version, IHL uint8) {
|
||||
v := ifrm.buf[0]
|
||||
return v >> 4, v & 0xf
|
||||
}
|
||||
|
||||
// SetVersionAndIHL sets the version and IHL fields in the IPv4 header. Version should always be 4.
|
||||
func (ifrm Frame) SetVersionAndIHL(version, IHL uint8) { ifrm.buf[0] = version<<4 | IHL&0xf }
|
||||
|
||||
// ToS (Type of Service) contains Differential Services Code Point (DSCP) and
|
||||
// Explicit Congestion Notification (ECN) union data.
|
||||
//
|
||||
// DSCP originally defined as the type of service (ToS), this field specifies
|
||||
// differentiated services (DiffServ) per RFC 2474. Real-time data streaming
|
||||
// makes use of the DSCP field. An example is Voice over IP (VoIP), which is
|
||||
// used for interactive voice services.
|
||||
//
|
||||
// ECN is defined in RFC 3168 and allows end-to-end notification of
|
||||
// network congestion without dropping packets. ECN is an optional feature available
|
||||
// when both endpoints support it and effective when also supported by the underlying network.
|
||||
func (ifrm Frame) ToS() ToS {
|
||||
return ToS(ifrm.buf[1])
|
||||
}
|
||||
|
||||
// SetToS sets ToS field. See [Frame.ToS].
|
||||
func (ifrm Frame) SetToS(tos ToS) { ifrm.buf[1] = byte(tos) }
|
||||
|
||||
// TotalLength defines the entire packet size in bytes, including IP header and data.
|
||||
// The minimum size is 20 bytes (IPv4 header without data) and the maximum is 65,535 bytes.
|
||||
// All hosts are required to be able to reassemble datagrams of size up to 576 bytes,
|
||||
// but most modern hosts handle much larger packets.
|
||||
//
|
||||
// Links may impose further restrictions on the packet size, in which case datagrams
|
||||
// must be fragmented. Fragmentation in IPv4 is performed in either the
|
||||
// sending host or in routers. Reassembly is performed at the receiving host.
|
||||
func (ifrm Frame) TotalLength() uint16 {
|
||||
return binary.BigEndian.Uint16(ifrm.buf[2:4])
|
||||
}
|
||||
|
||||
// SetTotalLength sets TotalLength field. See [Frame.TotalLength].
|
||||
func (ifrm Frame) SetTotalLength(tl uint16) { binary.BigEndian.PutUint16(ifrm.buf[2:4], tl) }
|
||||
|
||||
// ID is an identification field and is primarily used for uniquely
|
||||
// identifying the group of fragments of a single IP datagram.
|
||||
func (ifrm Frame) ID() uint16 {
|
||||
return binary.BigEndian.Uint16(ifrm.buf[4:6])
|
||||
}
|
||||
|
||||
// SetID sets ID field. See [Frame.ID].
|
||||
func (ifrm Frame) SetID(id uint16) { binary.BigEndian.PutUint16(ifrm.buf[4:6], id) }
|
||||
|
||||
// Flags returns the [Flags] of the IP packet.
|
||||
func (ifrm Frame) Flags() Flags {
|
||||
return Flags(binary.BigEndian.Uint16(ifrm.buf[6:8]))
|
||||
}
|
||||
|
||||
// SetFlags sets the IPv4 flags field. See [Flags].
|
||||
func (ifrm Frame) SetFlags(flags Flags) {
|
||||
binary.BigEndian.PutUint16(ifrm.buf[6:8], uint16(flags))
|
||||
}
|
||||
|
||||
// TTL is an eight-bit time to live field limits a datagram's lifetime to prevent
|
||||
// network failure in the event of a routing loop. In practice, the field
|
||||
// is used as a hop count—when the datagram arrives at a router,
|
||||
// the router decrements the TTL field by one. When the TTL field hits zero,
|
||||
// the router discards the packet and typically sends an ICMP time exceeded message to the sender.
|
||||
func (ifrm Frame) TTL() uint8 { return ifrm.buf[8] }
|
||||
|
||||
// SetTTL sets the IP frame's TTL field. See [Frame.TTL].
|
||||
func (ifrm Frame) SetTTL(ttl uint8) { ifrm.buf[8] = ttl }
|
||||
|
||||
// Protocol field defines the protocol used in the data portion of the IP datagram. TCP is 6, UDP is 17.
|
||||
// See [IPProto].
|
||||
func (ifrm Frame) Protocol() lneto2.IPProto { return lneto2.IPProto(ifrm.buf[9]) }
|
||||
|
||||
// SetProtocol sets protocol field. See [Frame.Protocol] and [lneto2.IPProto].
|
||||
func (ifrm Frame) SetProtocol(proto lneto2.IPProto) { ifrm.buf[9] = uint8(proto) }
|
||||
|
||||
// CRC returns the cyclic-redundancy-check (checksum) field of the IPv4 header.
|
||||
func (ifrm Frame) CRC() uint16 {
|
||||
return binary.BigEndian.Uint16(ifrm.buf[10:12])
|
||||
}
|
||||
|
||||
// SetCRC sets the CRC field of the IP packet. See [Frame.CRC].
|
||||
func (ifrm Frame) SetCRC(cs uint16) {
|
||||
binary.BigEndian.PutUint16(ifrm.buf[10:12], cs)
|
||||
}
|
||||
|
||||
// CalculateHeaderCRC calculates the CRC for this IPv4 frame.
|
||||
func (ifrm Frame) CalculateHeaderCRC() uint16 {
|
||||
var crc lneto2.CRC791
|
||||
crc.Write(ifrm.buf[0:10])
|
||||
crc.Write(ifrm.buf[12:20])
|
||||
return crc.Sum16()
|
||||
}
|
||||
|
||||
func (ifrm Frame) crcWriteTCPPseudo(crc *lneto2.CRC791) {
|
||||
crc.Write(ifrm.SourceAddr()[:])
|
||||
crc.Write(ifrm.DestinationAddr()[:])
|
||||
crc.AddUint16(ifrm.TotalLength() - 4*uint16(ifrm.ihl()))
|
||||
crc.AddUint16(uint16(ifrm.Protocol()))
|
||||
}
|
||||
|
||||
func (ifrm Frame) crcWriteUDPPseudo(crc *lneto2.CRC791) {
|
||||
crc.Write(ifrm.SourceAddr()[:])
|
||||
crc.Write(ifrm.DestinationAddr()[:])
|
||||
crc.AddUint16(uint16(ifrm.Protocol()))
|
||||
}
|
||||
|
||||
// SourceAddr returns pointer to the source IPv4 address in the IP header.
|
||||
func (ifrm Frame) SourceAddr() *[4]byte {
|
||||
return (*[4]byte)(ifrm.buf[12:16])
|
||||
}
|
||||
|
||||
// DestinationAddr returns pointer to the destination IPv4 address in the IP header.
|
||||
func (ifrm Frame) DestinationAddr() *[4]byte {
|
||||
return (*[4]byte)(ifrm.buf[16:20])
|
||||
}
|
||||
|
||||
// Payload returns the contents of the IPv4 packet, which may be zero sized.
|
||||
// Be sure to call [Frame.ValidateSize] beforehand to avoid panic.
|
||||
func (ifrm Frame) Payload() []byte {
|
||||
off := ifrm.HeaderLength()
|
||||
l := ifrm.TotalLength()
|
||||
return ifrm.buf[off:l]
|
||||
}
|
||||
|
||||
// Options returns the options portion of the IPv4 header. May be zero lengthed.
|
||||
// Be sure to call [Frame.ValidateSize] beforehand to avoid panic.
|
||||
func (ifrm Frame) Options() []byte {
|
||||
off := ifrm.HeaderLength()
|
||||
return ifrm.buf[sizeHeader:off]
|
||||
}
|
||||
|
||||
// ClearHeader zeros out the fixed(non-variable) header contents.
|
||||
func (ifrm Frame) ClearHeader() {
|
||||
for i := range ifrm.buf[:sizeHeader] {
|
||||
ifrm.buf[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Validation API.
|
||||
//
|
||||
|
||||
var (
|
||||
errBadTL = errors.New("ipv4: bad total length")
|
||||
errShort = errors.New("ipv4: short data")
|
||||
errBadIHL = errors.New("ipv4: bad IHL")
|
||||
errBadVersion = errors.New("ipv4: bad version")
|
||||
errEvil = errors.New("ipv4: evil packet")
|
||||
)
|
||||
|
||||
// ValidateSize checks the frame's size fields and compares with the actual buffer
|
||||
// the frame. It returns a non-nil error on finding an inconsistency.
|
||||
func (ifrm Frame) ValidateSize(v *lneto2.Validator) {
|
||||
ihl := ifrm.ihl()
|
||||
tl := ifrm.TotalLength()
|
||||
if tl < sizeHeader {
|
||||
v.AddError(errBadTL)
|
||||
}
|
||||
if int(tl) > len(ifrm.RawData()) {
|
||||
v.AddError(errShort)
|
||||
}
|
||||
if ihl < 5 {
|
||||
v.AddError(errBadIHL)
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateExceptCRC checks for invalid frame values but does not check CRC.
|
||||
func (ifrm Frame) ValidateExceptCRC(v *lneto2.Validator) {
|
||||
ifrm.ValidateSize(v)
|
||||
flags := ifrm.Flags()
|
||||
if ifrm.version() != 4 {
|
||||
v.AddError(errBadVersion)
|
||||
}
|
||||
if v.Flags()&lneto2.ValidateEvilBit != 0 && flags.IsEvil() {
|
||||
v.AddError(errEvil)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user