mirror of
https://github.com/soypat/lneto.git
synced 2026-08-11 10:23:47 +00:00
7d5830d7ab
* begin adding udp.MuxHandler * add udp MuxHandlerSIMO/MIMO * add tcp rx shutdown * icmpv6 client * icmpv6 Client shared NDP/Echo preparation * icmpv6 client ndp/echo split * icmpv6 client ndp/echo split done * icmpv6 adjustments * add dhcpv6 stubs * dhcpv4 preliminary revision * add dns.NextLabel * dns label name tweaks * dns begin work on TCP client * add dnstcp package * apply gofmt changes * add udp mux tests * clean up, remove StackBig for now * remove dnstcp so as to merged confident parts and we continue dnstcp work elsewhere
194 lines
5.0 KiB
Go
194 lines
5.0 KiB
Go
package icmpv6
|
|
|
|
import (
|
|
"slices"
|
|
|
|
"github.com/soypat/lneto"
|
|
"github.com/soypat/lneto/internal"
|
|
)
|
|
|
|
func (client *Client) PingIncomingCapacity() int {
|
|
return cap(client.incomingEcho)
|
|
}
|
|
|
|
func (client *Client) PingStart(remoteAddr [16]byte, pattern []byte, size uint16) (key uint32, err error) {
|
|
if int(size) < len(pattern) || len(pattern) == 0 {
|
|
return 0, lneto.ErrInvalidConfig
|
|
} else if remoteAddr == [16]byte{} {
|
|
return 0, lneto.ErrZeroDestination
|
|
}
|
|
free := cap(client.outgoingEcho) - len(client.outgoingEcho)
|
|
if free == 0 {
|
|
return 0, lneto.ErrExhausted
|
|
}
|
|
key = client.magichash(pattern, int(size)) & keyHashBits
|
|
v := internal.SliceReclaim(&client.outgoingEcho)
|
|
v.key = key
|
|
v.size = size
|
|
v.pattern = append(v.pattern[:0], pattern...)
|
|
v.raddr = remoteAddr
|
|
return key, nil
|
|
}
|
|
|
|
func (client *Client) PingPeek(key uint32) (completed, ok bool) {
|
|
idx := client.pingidx(key)
|
|
if idx >= 0 {
|
|
return client.outgoingEcho[idx].key&keyHashCompletedBit != 0, true
|
|
}
|
|
return false, false
|
|
}
|
|
|
|
func (client *Client) PingPop(key uint32) (completed, ok bool) {
|
|
idx := client.pingidx(key)
|
|
if idx >= 0 {
|
|
completed := client.outgoingEcho[idx].key&keyHashCompletedBit != 0
|
|
client.outgoingEcho = slices.Delete(client.outgoingEcho, idx, idx+1)
|
|
return completed, true
|
|
}
|
|
return false, false
|
|
}
|
|
|
|
func (client *Client) pingidx(key uint32) int {
|
|
for i := range client.outgoingEcho {
|
|
if client.outgoingEcho[i].key&keyHashBits == key {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
func (client *Client) seq() uint16 {
|
|
client._seq++
|
|
return client._seq
|
|
}
|
|
|
|
func (client *Client) magichash(pattern []byte, size int) (hash uint32) {
|
|
hash = client.magic
|
|
i := 0
|
|
n := size / len(pattern)
|
|
for i < n {
|
|
for _, b := range pattern {
|
|
hash = hash*31 + uint32(b)
|
|
}
|
|
i++
|
|
}
|
|
n = size % len(pattern)
|
|
for i = 0; i < n; i++ {
|
|
hash = hash*31 + uint32(pattern[i])
|
|
}
|
|
return hash
|
|
}
|
|
|
|
// demuxEcho handles TypeEchoRequest and TypeEchoReply frames.
|
|
// CRC has already been verified by the caller (Client.Demux).
|
|
func (client *Client) demuxEcho(carrierData []byte, frameOffset int) error {
|
|
rawdata := carrierData[frameOffset:]
|
|
ifrm, _ := NewFrame(rawdata) // already validated by Demux
|
|
tp := ifrm.Type()
|
|
ipEnabled := frameOffset >= 40
|
|
var raddr [16]byte
|
|
if ipEnabled {
|
|
src, _, _, _, _ := internal.GetIPAddr(carrierData)
|
|
if len(src) == 16 {
|
|
raddr = [16]byte(src)
|
|
}
|
|
}
|
|
var err error
|
|
switch tp {
|
|
case TypeEchoRequest:
|
|
free := cap(client.incomingEcho) - len(client.incomingEcho)
|
|
if free == 0 {
|
|
return lneto.ErrExhausted
|
|
}
|
|
efrm := FrameEcho{Frame: ifrm}
|
|
data := efrm.Data()
|
|
if len(data) == 0 {
|
|
return lneto.ErrPacketDrop
|
|
}
|
|
n, werr := client.responseRing.Write(data)
|
|
if werr != nil {
|
|
err = werr
|
|
break
|
|
}
|
|
v := internal.SliceReclaim(&client.incomingEcho)
|
|
v.length = uint16(n)
|
|
v.id = efrm.Identifier()
|
|
v.seq = efrm.SequenceNumber()
|
|
v.raddr = raddr
|
|
|
|
case TypeEchoReply:
|
|
efrm := FrameEcho{Frame: ifrm}
|
|
data := efrm.Data()
|
|
if len(data) == 0 {
|
|
return lneto.ErrPacketDrop
|
|
}
|
|
hash := client.magichash(data, len(data)) & keyHashBits
|
|
idx := client.pingidx(hash)
|
|
if idx < 0 || (ipEnabled && client.outgoingEcho[idx].raddr != raddr) {
|
|
err = lneto.ErrPacketDrop
|
|
break
|
|
}
|
|
client.outgoingEcho[idx].key |= keyHashCompletedBit
|
|
|
|
default:
|
|
err = lneto.ErrPacketDrop
|
|
}
|
|
return err
|
|
}
|
|
|
|
// encapsEcho writes an echo reply or request frame into carrierData[frameOffset:].
|
|
// CRC and SetIPAddrs are handled by the caller (Client.Encapsulate).
|
|
func (client *Client) encapsEcho(carrierData []byte, frameOffset int) (n int, dst [16]byte, err error) {
|
|
if len(client.incomingEcho) == 0 && len(client.outgoingEcho) == 0 {
|
|
return 0, [16]byte{}, nil
|
|
}
|
|
ifrm, err := NewFrame(carrierData[frameOffset:])
|
|
if err != nil {
|
|
return 0, [16]byte{}, err
|
|
}
|
|
if len(client.incomingEcho) > 0 {
|
|
// Priority: send echo reply.
|
|
inc := client.incomingEcho[0]
|
|
efrm := FrameEcho{Frame: ifrm}
|
|
efrm.SetType(TypeEchoReply)
|
|
efrm.SetIdentifier(inc.id)
|
|
efrm.SetSequenceNumber(inc.seq)
|
|
dataLen := int(inc.length)
|
|
_, rerr := client.responseRing.Read(efrm.Data()[:dataLen])
|
|
if rerr != nil {
|
|
return 0, [16]byte{}, rerr
|
|
}
|
|
client.incomingEcho = slices.Delete(client.incomingEcho, 0, 1)
|
|
n = sizeHeader + dataLen
|
|
dst = inc.raddr
|
|
} else {
|
|
idx := 0
|
|
for idx < len(client.outgoingEcho) && client.outgoingEcho[idx].key&keyHashSentBit != 0 {
|
|
idx++
|
|
}
|
|
if idx >= len(client.outgoingEcho) {
|
|
return 0, [16]byte{}, nil // No pending packet to send.
|
|
}
|
|
out := &client.outgoingEcho[idx]
|
|
efrm := FrameEcho{Frame: ifrm}
|
|
efrm.SetType(TypeEchoRequest)
|
|
efrm.SetIdentifier(client.id)
|
|
efrm.SetSequenceNumber(client.seq())
|
|
pattern := out.pattern
|
|
data := efrm.Data()
|
|
size := int(out.size)
|
|
written := 0
|
|
for written+len(pattern) <= size && written+len(pattern) <= len(data) {
|
|
copy(data[written:], pattern)
|
|
written += len(pattern)
|
|
}
|
|
copy(data[written:written+size%len(pattern)], pattern)
|
|
n = sizeHeader + size
|
|
out.key |= keyHashSentBit
|
|
dst = out.raddr
|
|
}
|
|
ifrm.buf = carrierData[frameOffset : frameOffset+n]
|
|
ifrm.SetCode(0)
|
|
return n, dst, nil
|
|
}
|