mirror of
https://github.com/soypat/lneto.git
synced 2026-07-26 10:38:47 +00:00
a42f0b404c
* feature: ipv4 broadcast support * stackip4 method receiver varname
184 lines
4.9 KiB
Go
184 lines
4.9 KiB
Go
package internet
|
|
|
|
import (
|
|
"log/slog"
|
|
|
|
"github.com/soypat/lneto"
|
|
"github.com/soypat/lneto/ethernet"
|
|
"github.com/soypat/lneto/internal"
|
|
"github.com/soypat/lneto/ipv6"
|
|
"github.com/soypat/lneto/tcp"
|
|
"github.com/soypat/lneto/udp"
|
|
)
|
|
|
|
// stackip6 is NOT a StackNode implementation.
|
|
// It is meant to be embedded within StackNodes.
|
|
// var _ lneto.StackNode = (*stackip6)(nil)
|
|
type StackIPv6 struct {
|
|
connID uint64
|
|
stackip6
|
|
}
|
|
|
|
func (stackip6 *StackIPv6) Reset(vld *lneto.Validator, maxNodes int) error {
|
|
stackip6.connID++
|
|
stackip6.reset6(vld, maxNodes)
|
|
return nil
|
|
}
|
|
|
|
func (stackip6 *StackIPv6) ConnectionID() *uint64 {
|
|
return &stackip6.connID
|
|
}
|
|
|
|
func (stackip6 *StackIPv6) Protocol() uint64 {
|
|
return uint64(ethernet.TypeIPv6)
|
|
}
|
|
|
|
func (stackip6 *StackIPv6) LocalPort() uint16 { return 0 }
|
|
|
|
func (stackip6 *StackIPv6) SetLogger(logger *slog.Logger) {
|
|
stackip6.stackip6.handlers.log = logger
|
|
}
|
|
|
|
func (stackip6 *StackIPv6) Demux(carrierData []byte, offset int) error {
|
|
debugLog("ip:demux")
|
|
return stackip6.stackip6.demux6(carrierData, offset)
|
|
}
|
|
|
|
func (stackip6 *StackIPv6) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int) (n int, err error) {
|
|
if offsetToFrame != offsetToIP {
|
|
return 0, lneto.ErrBug
|
|
}
|
|
return stackip6.stackip6.encapsulate6(carrierData, offsetToIP)
|
|
}
|
|
|
|
type stackip6 struct {
|
|
handlers handlers
|
|
vld *lneto.Validator
|
|
ip6 [16]byte
|
|
acceptMulticast bool
|
|
acceptBroadcast bool
|
|
}
|
|
|
|
func (si6 *stackip6) Register6(h lneto.StackNode) error {
|
|
proto := h.Protocol()
|
|
if proto > 255 {
|
|
return lneto.ErrInvalidConfig
|
|
}
|
|
return si6.handlers.registerByPortProto(nodeFromStackNode(h, h.LocalPort(), proto, nil))
|
|
}
|
|
|
|
func (si6 *stackip6) IsRegistered6(proto lneto.IPProto) bool {
|
|
return si6.handlers.nodeByProto(uint16(proto)) != nil
|
|
}
|
|
|
|
func (si6 *stackip6) SetAcceptMulticast6(accept bool) { si6.acceptMulticast = accept }
|
|
func (si6 *stackip6) Addr6() [16]byte { return si6.ip6 }
|
|
func (si6 *stackip6) SetAddr6(ip6 [16]byte) { si6.ip6 = ip6 }
|
|
|
|
func (si6 *stackip6) reset6(vld *lneto.Validator, maxNodes int) {
|
|
*si6 = stackip6{
|
|
handlers: si6.handlers,
|
|
vld: vld,
|
|
}
|
|
si6.handlers.reset("stackip6", maxNodes)
|
|
}
|
|
|
|
func (si6 *stackip6) demux6(carrierData []byte, offset int) error {
|
|
debugLog("ip6:demux")
|
|
si6.handlers.info("StackIP6.Demux:start")
|
|
ifrm, err := ipv6.NewFrame(carrierData[offset:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
dst := ifrm.DestinationAddr()
|
|
if si6.ip6 != ([16]byte{}) && *dst != si6.ip6 {
|
|
if !si6.acceptMulticast || !internal.IsMulticastIPAddr(dst[:]) {
|
|
si6.handlers.debug("ip6:not-for-us")
|
|
return lneto.ErrPacketDrop
|
|
}
|
|
}
|
|
|
|
si6.vld.ResetErr()
|
|
ifrm.ValidateSize(si6.vld)
|
|
if err = si6.vld.ErrPop(); err != nil {
|
|
si6.handlers.error("ip6:Demux.validate")
|
|
return err
|
|
}
|
|
|
|
proto := ifrm.NextHeader()
|
|
node := si6.handlers.nodeByProto(uint16(proto))
|
|
if node == nil {
|
|
si6.handlers.info("ip6:demux.drop", slog.String("proto", proto.String()))
|
|
return lneto.ErrPacketDrop
|
|
}
|
|
payload := ifrm.Payload()
|
|
var crc lneto.CRC791
|
|
switch proto {
|
|
case lneto.IPProtoTCP:
|
|
ifrm.CRCWritePseudo(&crc)
|
|
if crc.PayloadSum16(payload) != 0 {
|
|
si6.handlers.error("ip6:demux.tcpcrc")
|
|
return lneto.ErrBadCRC
|
|
}
|
|
case lneto.IPProtoUDP:
|
|
ufrm, err := udp.NewFrame(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ufrm.ValidateSize(si6.vld)
|
|
if err = si6.vld.ErrPop(); err != nil {
|
|
si6.handlers.error("ip6:demux.udpvalidatesize")
|
|
return err
|
|
}
|
|
ifrm.CRCWritePseudo(&crc)
|
|
if crc.PayloadSum16(payload) != 0 {
|
|
si6.handlers.error("ip6:demux.udpcrc")
|
|
return lneto.ErrBadCRC
|
|
}
|
|
}
|
|
const headerlen = 40
|
|
plen := ifrm.PayloadLength()
|
|
si6.handlers.info("ip6Demux", slog.String("ipproto", proto.String()), slog.Int("plen", int(plen)))
|
|
err = node.callbacks.Demux(carrierData[offset:offset+headerlen+int(plen)], headerlen)
|
|
if si6.handlers.tryHandleError(node, err) {
|
|
si6.handlers.info("ip6close", slog.String("proto", proto.String()))
|
|
err = nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (si6 *stackip6) encapsulate6(carrierData []byte, offsetToIP int) (int, error) {
|
|
ifrm, err := ipv6.NewFrame(carrierData[offsetToIP:])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
// Set default parameters which node is free to change.
|
|
ifrm.SetVersionTrafficAndFlow(6, 0, 0)
|
|
ifrm.SetHopLimit(64)
|
|
*ifrm.SourceAddr() = si6.ip6
|
|
const headerlen = 40
|
|
node, n, err := si6.handlers.encapsulateAny(carrierData, offsetToIP, offsetToIP+headerlen)
|
|
if n == 0 {
|
|
return n, err
|
|
}
|
|
proto := lneto.IPProto(node.proto)
|
|
ifrm.SetNextHeader(proto)
|
|
ifrm.SetPayloadLength(uint16(n))
|
|
var crc lneto.CRC791
|
|
payload := ifrm.Payload()
|
|
switch proto {
|
|
case lneto.IPProtoTCP:
|
|
ifrm.CRCWritePseudo(&crc)
|
|
tfrm, _ := tcp.NewFrame(payload)
|
|
tfrm.SetCRC(0)
|
|
tfrm.SetCRC(crc.PayloadSum16(payload))
|
|
case lneto.IPProtoUDP:
|
|
ufrm, _ := udp.NewFrame(payload)
|
|
ufrm.SetLength(uint16(n))
|
|
ifrm.CRCWritePseudo(&crc)
|
|
ufrm.SetCRC(0)
|
|
ufrm.SetCRC(lneto.NeverZeroSum(crc.PayloadSum16(payload)))
|
|
}
|
|
return headerlen + n, err
|
|
}
|