mirror of
https://github.com/soypat/lneto.git
synced 2026-09-10 16:49:37 +00:00
add lneto.Validator type for frame validation
This commit is contained in:
@@ -0,0 +1,77 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/soypat/lneto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type LinkStack struct {
|
||||||
|
mac [6]byte
|
||||||
|
mtu uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *LinkStack) RecvEth(ethFrame []byte) (err error) {
|
||||||
|
eframe, err := lneto.NewEthFrame(ethFrame)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !eframe.IsBroadcast() && ls.mac != *eframe.DestinationHardwareAddr() {
|
||||||
|
return errors.New("packet MAC mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to dynamic handling.
|
||||||
|
etype := eframe.EtherTypeOrSize()
|
||||||
|
if etype != lneto.EtherTypeARP && etype != lneto.EtherTypeIPv4 && etype != lneto.EtherTypeIPv6 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *LinkStack) HandleEth(dst []byte) (n int, err error) {
|
||||||
|
if len(dst) < int(ls.mtu) {
|
||||||
|
return 0, io.ErrShortBuffer
|
||||||
|
}
|
||||||
|
n, addr, etype, err := ls.handleUpper(dst[14:])
|
||||||
|
if err != nil || n == 0 {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
eframe, _ := lneto.NewEthFrame(dst[:14])
|
||||||
|
*eframe.DestinationHardwareAddr() = addr
|
||||||
|
*eframe.SourceHardwareAddr() = ls.mac
|
||||||
|
eframe.SetEtherType(etype)
|
||||||
|
return 14 + n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *LinkStack) handleUpper(dst []byte) (n int, dstAddr [6]byte, etype lneto.EtherType, err error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type IPv4Stack struct {
|
||||||
|
ip [4]byte
|
||||||
|
mtu uint16
|
||||||
|
validator lneto.Validator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *IPv4Stack) Recv(ipframe []byte) error {
|
||||||
|
iframe, err := lneto.NewIPv4Frame(ipframe)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if *iframe.DestinationAddr() != is.ip {
|
||||||
|
return errors.New("packet not for us")
|
||||||
|
}
|
||||||
|
iframe.Validate(&is.validator)
|
||||||
|
err = is.validator.Err()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
@@ -111,6 +111,12 @@ func (efrm EthFrame) DestinationHardwareAddr() (dst *[6]byte) {
|
|||||||
return (*[6]byte)(efrm.buf[0:6])
|
return (*[6]byte)(efrm.buf[0:6])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsBroadcast returns true if the destination is the broadcast address ff:ff:ff:ff:ff:ff, false otherwise.
|
||||||
|
func (efrm EthFrame) IsBroadcast() bool {
|
||||||
|
return efrm.buf[0] == 0xff && efrm.buf[1] == 0xff && efrm.buf[2] == 0xff &&
|
||||||
|
efrm.buf[3] == 0xff && efrm.buf[4] == 0xff && efrm.buf[5] == 0xff
|
||||||
|
}
|
||||||
|
|
||||||
// SourceHardwareAddr returns the sender's MAC/hardware address of the ethernet packet.
|
// SourceHardwareAddr returns the sender's MAC/hardware address of the ethernet packet.
|
||||||
func (efrm EthFrame) SourceHardwareAddr() (src *[6]byte) {
|
func (efrm EthFrame) SourceHardwareAddr() (src *[6]byte) {
|
||||||
return (*[6]byte)(efrm.buf[6:12])
|
return (*[6]byte)(efrm.buf[6:12])
|
||||||
@@ -267,9 +273,8 @@ func (ifrm IPv4Frame) HeaderLength() int {
|
|||||||
return int(ifrm.ihl()) * 4
|
return int(ifrm.ihl()) * 4
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ifrm IPv4Frame) ihl() uint8 {
|
func (ifrm IPv4Frame) ihl() uint8 { return ifrm.buf[0] & 0xf }
|
||||||
return ifrm.buf[0] & 0xf
|
func (ifrm IPv4Frame) version() uint8 { return ifrm.buf[0] >> 4 }
|
||||||
}
|
|
||||||
|
|
||||||
// VersionAndIHL returns the version and IHL fields in the IPv4 header. Version should always be 4.
|
// VersionAndIHL returns the version and IHL fields in the IPv4 header. Version should always be 4.
|
||||||
func (ifrm IPv4Frame) VersionAndIHL() (version, IHL uint8) {
|
func (ifrm IPv4Frame) VersionAndIHL() (version, IHL uint8) {
|
||||||
|
|||||||
+7
-6
@@ -217,16 +217,17 @@ func (gen *packetGen) appendRandomIPv4TCPPacket(dst []byte, rng *rand.Rand) []by
|
|||||||
case len(tcpPayload) > 0 && firstPayloadByte != tcpPayload[0]:
|
case len(tcpPayload) > 0 && firstPayloadByte != tcpPayload[0]:
|
||||||
panic("TCP options overwrite payload")
|
panic("TCP options overwrite payload")
|
||||||
}
|
}
|
||||||
err = efrm.ValidateSize()
|
var vld Validator
|
||||||
if err != nil {
|
efrm.ValidateSize(&vld)
|
||||||
|
if err = vld.Err(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
err = ifrm.ValidateSize()
|
ifrm.Validate(&vld)
|
||||||
if err != nil {
|
if err = vld.Err(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
err = tfrm.ValidateSize()
|
tfrm.ValidateSize(&vld)
|
||||||
if err != nil {
|
if err = vld.Err(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return dst
|
return dst
|
||||||
|
|||||||
+10
-7
@@ -578,6 +578,7 @@ func TestExchange_helloworld_client(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func parseSegment(t *testing.T, b []byte) (tcp.Segment, []byte) {
|
func parseSegment(t *testing.T, b []byte) (tcp.Segment, []byte) {
|
||||||
|
var vld lneto.Validator
|
||||||
t.Helper()
|
t.Helper()
|
||||||
efrm, err := lneto.NewEthFrame(b)
|
efrm, err := lneto.NewEthFrame(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -586,9 +587,9 @@ func parseSegment(t *testing.T, b []byte) (tcp.Segment, []byte) {
|
|||||||
if efrm.EtherTypeOrSize() != lneto.EtherTypeIPv4 {
|
if efrm.EtherTypeOrSize() != lneto.EtherTypeIPv4 {
|
||||||
t.Fatalf("not IPv4")
|
t.Fatalf("not IPv4")
|
||||||
}
|
}
|
||||||
err = efrm.ValidateSize()
|
efrm.ValidateSize(&vld)
|
||||||
if err != nil {
|
if err := vld.Err(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(vld.Err())
|
||||||
}
|
}
|
||||||
ifrm, err := lneto.NewIPv4Frame(efrm.Payload())
|
ifrm, err := lneto.NewIPv4Frame(efrm.Payload())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -601,16 +602,18 @@ func parseSegment(t *testing.T, b []byte) (tcp.Segment, []byte) {
|
|||||||
if v != 4 {
|
if v != 4 {
|
||||||
t.Fatal("invalid IP version", v)
|
t.Fatal("invalid IP version", v)
|
||||||
}
|
}
|
||||||
err = ifrm.ValidateSize()
|
ifrm.ValidateSize(&vld)
|
||||||
if err != nil {
|
if err := vld.Err(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(vld.Err())
|
||||||
}
|
}
|
||||||
|
|
||||||
ipl := ifrm.Payload()
|
ipl := ifrm.Payload()
|
||||||
tfrm, err := lneto.NewTCPFrame(ipl)
|
tfrm, err := lneto.NewTCPFrame(ipl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if err = tfrm.ValidateSize(); err != nil {
|
}
|
||||||
|
tfrm.ValidateSize(&vld)
|
||||||
|
if err := vld.Err(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_ = tfrm.String()
|
_ = tfrm.String()
|
||||||
|
|||||||
+86
-46
@@ -14,77 +14,117 @@ var (
|
|||||||
errShortARP = errors.New("bad ARP size")
|
errShortARP = errors.New("bad ARP size")
|
||||||
errShortTCP = errors.New("TCP offset exceeds frame")
|
errShortTCP = errors.New("TCP offset exceeds frame")
|
||||||
errBadTCPOff = errors.New("TCP offset invalid")
|
errBadTCPOff = errors.New("TCP offset invalid")
|
||||||
|
|
||||||
|
errBadIPVersion = errors.New("bad IP version field")
|
||||||
|
errEvilPacket = errors.New("evil packet")
|
||||||
)
|
)
|
||||||
|
|
||||||
// ValidateSize checks the frame's size fields and compares with the actual buffer
|
type Validator struct {
|
||||||
// the frame. It returns a non-nil error on finding an inconsistency.
|
checkEvil bool
|
||||||
func (efrm EthFrame) ValidateSize() error {
|
accum []error
|
||||||
sz := efrm.EtherTypeOrSize()
|
}
|
||||||
if sz.IsSize() && len(efrm.buf) < int(sz) {
|
|
||||||
return errShortEth
|
func (v *Validator) ResetErr() {
|
||||||
} else if sz == EtherTypeVLAN && len(efrm.buf) < 18 {
|
v.accum = v.accum[:0]
|
||||||
return errShortVLAN
|
}
|
||||||
|
|
||||||
|
func (v *Validator) Err() error {
|
||||||
|
if len(v.accum) == 1 {
|
||||||
|
return v.accum[0]
|
||||||
|
} else if len(v.accum) == 0 {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return nil
|
return errors.Join(v.accum...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Validator) gotErr(err error) {
|
||||||
|
v.accum = append(v.accum, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateSize checks the frame's size fields and compares with the actual buffer
|
// 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.
|
// the frame. It returns a non-nil error on finding an inconsistency.
|
||||||
func (afrm ARPFrame) ValidateSize() error {
|
func (efrm EthFrame) ValidateSize(v *Validator) {
|
||||||
|
sz := efrm.EtherTypeOrSize()
|
||||||
|
if sz.IsSize() && len(efrm.buf) < int(sz) {
|
||||||
|
v.gotErr(errShortEth)
|
||||||
|
}
|
||||||
|
if sz == EtherTypeVLAN && len(efrm.buf) < 18 {
|
||||||
|
v.gotErr(errShortVLAN)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 (afrm ARPFrame) ValidateSize(v *Validator) {
|
||||||
_, hlen := afrm.Hardware()
|
_, hlen := afrm.Hardware()
|
||||||
_, ilen := afrm.Protocol()
|
_, ilen := afrm.Protocol()
|
||||||
minLen := 8 + 2*(hlen+ilen)
|
minLen := 8 + 2*(hlen+ilen)
|
||||||
if len(afrm.buf) < int(minLen) {
|
if len(afrm.buf) < int(minLen) {
|
||||||
return errShortARP
|
v.gotErr(errShortARP)
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateSize checks the frame's size fields and compares with the actual buffer
|
// 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.
|
// the frame. It returns a non-nil error on finding an inconsistency.
|
||||||
func (ufrm UDPFrame) ValidateSize() error {
|
func (ifrm IPv4Frame) ValidateSize(v *Validator) {
|
||||||
ul := ufrm.Length()
|
|
||||||
if ul < sizeHeaderUDP {
|
|
||||||
return errBadUDPLen
|
|
||||||
} else if int(ul) > len(ufrm.RawData()) {
|
|
||||||
return errShortUDP
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 IPv4Frame) ValidateSize() error {
|
|
||||||
ihl := ifrm.ihl()
|
ihl := ifrm.ihl()
|
||||||
tl := ifrm.TotalLength()
|
tl := ifrm.TotalLength()
|
||||||
if tl < sizeHeaderIPv4 {
|
if tl < sizeHeaderIPv4 {
|
||||||
return errBadIPv4TL
|
v.gotErr(errBadIPv4TL)
|
||||||
} else if int(tl) > len(ifrm.RawData()) {
|
|
||||||
return errShortIPv4
|
|
||||||
} else if ihl < 5 {
|
|
||||||
return errBadIPv4IHL
|
|
||||||
}
|
}
|
||||||
return nil
|
if int(tl) > len(ifrm.RawData()) {
|
||||||
|
v.gotErr(errShortIPv4)
|
||||||
|
}
|
||||||
|
if ihl < 5 {
|
||||||
|
v.gotErr(errBadIPv4IHL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ifrm IPv4Frame) ValidateFields(v *Validator) {
|
||||||
|
flags := ifrm.Flags()
|
||||||
|
if ifrm.version() != 4 {
|
||||||
|
v.gotErr(errBadIPVersion)
|
||||||
|
}
|
||||||
|
if v.checkEvil && flags.IsEvil() {
|
||||||
|
v.gotErr(errEvilPacket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate checks for invalid frame values.
|
||||||
|
func (ifrm IPv4Frame) Validate(v *Validator) {
|
||||||
|
ifrm.ValidateSize(v)
|
||||||
|
ifrm.ValidateFields(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateSize checks the frame's size fields and compares with the actual buffer
|
// 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.
|
// the frame. It returns a non-nil error on finding an inconsistency.
|
||||||
func (tfrm TCPFrame) ValidateSize() error {
|
func (i6frm IPv6Frame) ValidateSize(v *Validator) {
|
||||||
off := tfrm.HeaderLength()
|
|
||||||
if off < sizeHeaderTCP {
|
|
||||||
return errBadTCPOff
|
|
||||||
} else if off > len(tfrm.RawData()) {
|
|
||||||
return errShortTCP
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 (i6frm IPv6Frame) ValidateSize() error {
|
|
||||||
tl := i6frm.PayloadLength()
|
tl := i6frm.PayloadLength()
|
||||||
if int(tl)+sizeHeaderIPv6 > len(i6frm.RawData()) {
|
if int(tl)+sizeHeaderIPv6 > len(i6frm.RawData()) {
|
||||||
return errShortIPv6
|
v.gotErr(errShortIPv6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 (tfrm TCPFrame) ValidateSize(v *Validator) {
|
||||||
|
off := tfrm.HeaderLength()
|
||||||
|
if off < sizeHeaderTCP {
|
||||||
|
v.gotErr(errBadTCPOff)
|
||||||
|
}
|
||||||
|
if off > len(tfrm.RawData()) {
|
||||||
|
v.gotErr(errShortTCP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 (ufrm UDPFrame) ValidateSize(v *Validator) {
|
||||||
|
ul := ufrm.Length()
|
||||||
|
if ul < sizeHeaderUDP {
|
||||||
|
v.gotErr(errBadUDPLen)
|
||||||
|
}
|
||||||
|
if int(ul) > len(ufrm.RawData()) {
|
||||||
|
v.gotErr(errShortUDP)
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user