added some option handling functions

This commit is contained in:
2025-09-26 19:58:22 +09:00
parent 90365bfdd4
commit 6ce8f1a5fe
5 changed files with 473 additions and 84 deletions

View File

@ -10,6 +10,7 @@ VERSION=1.0.0
SRCS=\ SRCS=\
haza.go \ haza.go \
opt.go \
pkt.go \ pkt.go \
server.go \ server.go \
sock.go sock.go

22
haza.go
View File

@ -81,19 +81,19 @@ func Ntoh32(v uint32) uint32 {
} }
// --------------------------------------------------------- // ---------------------------------------------------------
type ByteReader struct { type NetByteReader struct {
r *bytes.Reader r *bytes.Reader
} }
func NewByteReader(b []byte) *ByteReader { func NewNetByteReader(b []byte) *NetByteReader {
return &ByteReader{ r: bytes.NewReader(b) } return &NetByteReader{ r: bytes.NewReader(b) }
} }
func (br* ByteReader) ReadByte() (byte, error) { func (br* NetByteReader) ReadByte() (byte, error) {
return br.r.ReadByte() return br.r.ReadByte()
} }
func (br* ByteReader) ReadUint16() (uint16, error) { func (br* NetByteReader) ReadUint16() (uint16, error) {
var v uint16 var v uint16
var err error var err error
err = binary.Read(br.r, binary.BigEndian, &v) err = binary.Read(br.r, binary.BigEndian, &v)
@ -101,7 +101,7 @@ func (br* ByteReader) ReadUint16() (uint16, error) {
return v, nil return v, nil
} }
func (br* ByteReader) ReadUint32() (uint32, error) { func (br* NetByteReader) ReadUint32() (uint32, error) {
var v uint32 var v uint32
var err error var err error
err = binary.Read(br.r, binary.BigEndian, &v) err = binary.Read(br.r, binary.BigEndian, &v)
@ -109,7 +109,7 @@ func (br* ByteReader) ReadUint32() (uint32, error) {
return v, nil return v, nil
} }
func (br* ByteReader) ReadIp4() (net.IP, error) { func (br* NetByteReader) ReadIp4() (net.IP, error) {
var buf [4]byte var buf [4]byte
var err error var err error
_, err = io.ReadFull(br.r, buf[:]) _, err = io.ReadFull(br.r, buf[:])
@ -117,8 +117,14 @@ func (br* ByteReader) ReadIp4() (net.IP, error) {
return net.IP(buf[:]), nil return net.IP(buf[:]), nil
} }
func (br *ByteReader) ReadAllBytes(buf []byte) error { func (br *NetByteReader) ReadBytesFull(buf []byte) error {
var err error var err error
_, err = io.ReadFull(br.r, buf) _, err = io.ReadFull(br.r, buf)
return err return err
} }
func (br *NetByteReader) ReadBytesAtLeast(buf []byte, len int) error {
var err error
_, err = io.ReadAtLeast(br.r, buf, len)
return err
}

225
opt.go Normal file
View File

@ -0,0 +1,225 @@
package haza
import "encoding/binary"
import "net"
type Dhcp4OptCode = uint8
type Dhcp4MsgType = uint8
const (
_ Dhcp4MsgType = iota
DHCP4_MSG_DISCOVER = 1
DHCP4_MSG_OFFER = 2
DHCP4_MSG_REQUEST = 3
DHCP4_MSG_DECLINE = 4
DHCP4_MSG_ACK = 5
DHCP4_MSG_NAK = 6
DHCP4_MSG_RELEASE = 7
DHCP4_MSG_INFORM = 8
DHCP4_MSG_FORCE_RENEW = 9
DHCP4_MSG_LEASE_QUERY = 10
DHCP4_MSG_LEASE_UNASSIGNED = 11
DHCP4_MSG_LEASE_UNKNOWN = 12
DHCP4_MSG_LEASE_ACTIVE = 13
DHCP4_MSG_BULK_LEASE_QUERY = 14
DHCP4_MSG_LEASE_QUERY_DONE = 15
DHCP4_MSG_ACTIVE_LEASE_QUERY = 16
DHCP4_MSG_LEASE_QUERY_STATUS = 17
DHCP4_MSG_TLS = 18
)
const (
DHCP4_OPT_PADDING Dhcp4OptCode = iota // 0
DHCP4_OPT_SUBNET_MASK = 0x01
DHCP4_OPT_TIME_OFFSET = 0x02
DHCP4_OPT_ROUTER = 0x03
DHCP4_OPT_TIME_SERVER = 0x04
DHCP4_OPT_NAME_SERVER = 0x05
DHCP4_OPT_DNS_SERVER = 0x06
DHCP4_OPT_LOG_SERVER = 0x07
DHCP4_OPT_QUOTE_SERVER = 0x08
DHCP4_OPT_LPR_SERVER = 0x09
DHCP4_OPT_HOST_NAME = 0x0C
DHCP4_OPT_VENDOR_SPECIFIC = 0x2B
DHCP4_OPT_REQUESTED_IPADDR = 0x32
DHCP4_OPT_LEASE_TIME = 0x33
DHCP4_OPT_OVERLOAD = 0x34
DHCP4_OPT_MSG_TYPE = 0x35
DHCP4_OPT_SERVER_ID = 0x36
DHCP4_OPT_PARAM_REQ_LIST = 0x37
DHCP4_OPT_ERROR_TEXT = 0x38
DHCP4_OPT_MAX_MSG_SIZE = 0x39
DHCP4_OPT_RENEWAL_TIME = 0x3A // T1
DHCP4_OPT_REBINDING_TIME = 0x3B // T2
DHCP4_OPT_CLIENT_ID = 0x3D
DHCP4_OPT_END = 0xFF
)
const (
DHCP4_OPT_VAL_UINT8 = 0
)
type Dhcp4Opt interface {
Code() Dhcp4OptCode
Bytes() []byte
}
type Dhcp4OptBase struct {
code Dhcp4OptCode
}
type Dhcp4OptIp4Addr struct {
Dhcp4OptBase
value net.IP
}
type Dhcp4OptIp4Addrs struct {
Dhcp4OptBase
value []net.IP
}
type Dhcp4OptString struct {
Dhcp4OptBase
value string
}
type Dhcp4OptBytes struct {
Dhcp4OptBase
value []byte
}
type Dhcp4OptUint32 struct {
Dhcp4OptBase
value uint32
}
type Dhcp4OptUint16 struct {
Dhcp4OptBase
value uint16
}
type Dhcp4OptUint8 struct {
Dhcp4OptBase
value uint8
}
func (opt *Dhcp4OptBase) Code() Dhcp4OptCode {
return opt.code
}
func (opt *Dhcp4OptIp4Addr) Bytes() []byte {
// net.IP itself is []byte
var buf []byte
buf = opt.value.To4()
if buf == nil { return []byte{} }
return buf
}
func (opt *Dhcp4OptIp4Addrs) Bytes() []byte {
var buf []byte
var v net.IP
var v4 []byte
var count int
count = 0
for _, ip := range opt.value {
if ip.To4() != nil { count++ }
}
buf = make([]byte, 0, count * net.IPv4len)
for _, v = range opt.value {
v4 = v.To4()
if v4 == nil { continue }
buf = append(buf, v4...)
}
return buf
}
func (opt *Dhcp4OptString) Bytes() []byte {
return []byte(opt.value)
}
func (opt *Dhcp4OptBytes) Bytes() []byte {
return opt.value
}
func (opt *Dhcp4OptUint32) Bytes() []byte {
var buf [4]byte
binary.BigEndian.PutUint32(buf[:], opt.value)
return buf[:]
}
func (opt *Dhcp4OptUint16) Bytes() []byte {
var buf [2]byte
binary.BigEndian.PutUint16(buf[:], opt.value)
return buf[:]
}
func (opt *Dhcp4OptUint8) Bytes() []byte {
return []byte{opt.value}
}
func Dhcp4OptMsgType(v uint8) Dhcp4Opt {
return &Dhcp4OptUint8{Dhcp4OptBase: Dhcp4OptBase{code: DHCP4_OPT_MSG_TYPE}, value: v}
}
func Dhcp4OptSubnetMask(v net.IP) Dhcp4Opt {
return &Dhcp4OptIp4Addr{Dhcp4OptBase: Dhcp4OptBase{code: DHCP4_OPT_SUBNET_MASK}, value: v}
}
func Dhcp4OptRouter(v net.IP) Dhcp4Opt {
return &Dhcp4OptIp4Addr{Dhcp4OptBase: Dhcp4OptBase{code: DHCP4_OPT_ROUTER}, value: v}
}
func Dhcp4OptTimeServers(v []net.IP) Dhcp4Opt {
return &Dhcp4OptIp4Addrs{Dhcp4OptBase: Dhcp4OptBase{code: DHCP4_OPT_TIME_SERVER}, value: v}
}
func Dhcp4OptDnsServers(v []net.IP) Dhcp4Opt {
return &Dhcp4OptIp4Addrs{Dhcp4OptBase: Dhcp4OptBase{code: DHCP4_OPT_DNS_SERVER}, value: v}
}
func Dhcp4OptHostName(v string) Dhcp4Opt {
return &Dhcp4OptString{Dhcp4OptBase: Dhcp4OptBase{code: DHCP4_OPT_DNS_SERVER}, value: v}
}
func Dhcp4OptReqIpaddr(v net.IP) Dhcp4Opt {
return &Dhcp4OptIp4Addr{Dhcp4OptBase: Dhcp4OptBase{code: DHCP4_OPT_REQUESTED_IPADDR}, value: v}
}
func Dhcp4OptLeaseTime(v uint32) Dhcp4Opt {
return &Dhcp4OptUint32{Dhcp4OptBase: Dhcp4OptBase{code: DHCP4_OPT_LEASE_TIME}, value: v}
}
func Dhcp4OptServerId(v net.IP) Dhcp4Opt {
return &Dhcp4OptIp4Addr{Dhcp4OptBase: Dhcp4OptBase{code: DHCP4_OPT_SERVER_ID}, value: v}
}
func Dhcp4OptParamReqList(v []Dhcp4OptCode) Dhcp4Opt {
return &Dhcp4OptBytes{Dhcp4OptBase: Dhcp4OptBase{code: DHCP4_OPT_PARAM_REQ_LIST}, value: v}
}
func Dhcp4OptErrorText(v string) Dhcp4Opt {
return &Dhcp4OptString{Dhcp4OptBase: Dhcp4OptBase{code: DHCP4_OPT_ERROR_TEXT}, value: v}
}
func Dhcp4OptMaxMsgSize(v uint16) Dhcp4Opt {
return &Dhcp4OptUint16{Dhcp4OptBase: Dhcp4OptBase{code: DHCP4_OPT_MAX_MSG_SIZE}, value: v}
}
func Dhcp4OptRenewalTime(v uint32) Dhcp4Opt {
return &Dhcp4OptUint32{Dhcp4OptBase: Dhcp4OptBase{code: DHCP4_OPT_RENEWAL_TIME}, value: v}
}
func Dhcp4OptRebindingTime(v uint32) Dhcp4Opt {
return &Dhcp4OptUint32{Dhcp4OptBase: Dhcp4OptBase{code: DHCP4_OPT_REBINDING_TIME}, value: v}
}
func Dhcp4OptClientId(v string) Dhcp4Opt {
return &Dhcp4OptString{Dhcp4OptBase: Dhcp4OptBase{code: DHCP4_OPT_CLIENT_ID}, value: v}
}

198
pkt.go
View File

@ -1,10 +1,12 @@
package haza package haza
import "errors"
import "fmt"
import "io"
import "net" import "net"
type Dhcp4Op = uint8 type Dhcp4Op = uint8
type Dhcp4Htype = uint8 type Dhcp4Htype = uint8
type Dhcp4Msg = uint8
const ( const (
_ Dhcp4Op = iota _ Dhcp4Op = iota
@ -26,32 +28,6 @@ const (
DHCP4_HTYPE_PUREIP = 35 DHCP4_HTYPE_PUREIP = 35
) )
const (
_ Dhcp4Msg = iota
DHCP4_MSG_DISCOVER = 1
DHCP4_MSG_OFFER = 2
DHCP4_MSG_REQUEST = 3
DHCP4_MSG_DECLINE = 4
DHCP4_MSG_ACK = 5
DHCP4_MSG_NAK = 6
DHCP4_MSG_RELEASE = 7
DHCP4_MSG_INFORM = 8
DHCP4_MSG_FORCE_RENEW = 9
DHCP4_MSG_LEASE_QUERY = 10
DHCP4_MSG_LEASE_UNASSIGNED = 11
DHCP4_MSG_LEASE_UNKNOWN = 12
DHCP4_MSG_LEASE_ACTIVE = 13
DHCP4_MSG_BULK_LEASE_QUERY = 14
DHCP4_MSG_LEASE_QUERY_DONE = 15
DHCP4_MSG_ACTIVE_LEASE_QUERY = 16
DHCP4_MSG_LEASE_QUERY_STATUS = 17
DHCP4_MSG_TLS = 18
)
type Dhcp4Pkt struct { type Dhcp4Pkt struct {
Op Dhcp4Op Op Dhcp4Op
Htype Dhcp4Htype Htype Dhcp4Htype
@ -74,70 +50,168 @@ type Dhcp4Pkt struct {
// options are placed after the header. // options are placed after the header.
// the first four bytes of the options compose a magic cookie // the first four bytes of the options compose a magic cookie
// 0x63 0x82 0x53 0x63 // 0x63 0x82 0x53 0x63
Options map[Dhcp4OptCode][]byte
// internal fields
msg_type Dhcp4MsgType
}
var dhcp4_magic_cookie [4]byte = [4]byte{ 0x63, 0x82, 0x53, 0x63 }
func NewDhcp4Pkt(msg_type Dhcp4MsgType) *Dhcp4Pkt {
var pkt *Dhcp4Pkt
var opt Dhcp4Opt
pkt = &Dhcp4Pkt{
Op: DHCP4_OP_BOOTREQUEST, // TODO: choose between request and reply based on the msg type..
Htype: DHCP4_HTYPE_ETHER,
Hlen: 6,
Xid: 0, // TODO: generate it?
Ciaddr: net.IPv4zero,
Yiaddr: net.IPv4zero,
Siaddr: net.IPv4zero,
Gwaddr: net.IPv4zero,
//Chaddr:
//Sname:
//File:
// fill the internal fields
msg_type: msg_type,
}
// add the message type option
opt = Dhcp4OptMsgType(msg_type)
pkt.Options[opt.Code()] = opt.Bytes()
return pkt
}
func (pkt *Dhcp4Pkt) AddOption(opt Dhcp4Opt) {
pkt.Options[opt.Code()] = opt.Bytes();
} }
func (pkt *Dhcp4Pkt) Decode(b []byte) error { func (pkt *Dhcp4Pkt) Decode(b []byte) error {
// fill the packet with data from the bytes // fill the packet with data from the bytes
var r *ByteReader var nbr *NetByteReader
var u8 byte var u8 byte
var u16 uint16
var u32 uint32
var p Dhcp4Pkt var p Dhcp4Pkt
var cookie [4]byte
var ov []byte
var ok bool
var err error var err error
r = NewByteReader(b) nbr = NewNetByteReader(b)
u8, err = r.ReadByte() u8, err = nbr.ReadByte()
if err != nil { return err } if err != nil { return err }
p.Op = Dhcp4Op(u8) p.Op = Dhcp4Op(u8)
u8, err = r.ReadByte() u8, err = nbr.ReadByte()
if err != nil { return err } if err != nil { return err }
p.Htype = Dhcp4Htype(u8) p.Htype = Dhcp4Htype(u8)
u8, err = r.ReadByte() p.Hlen, err = nbr.ReadByte()
if err != nil { return err } if err != nil { return err }
p.Hlen = u8 if p.Hlen > 16 { return fmt.Errorf("invalid hlen %d", p.Hlen) }
u8, err = r.ReadByte() p.Hops, err = nbr.ReadByte()
if err != nil { return err }
p.Hops = u8
u32, err = r.ReadUint32()
if err != nil { return err }
p.Xid = u32
u16, err = r.ReadUint16()
if err != nil { return err }
p.Secs = u16
u16, err = r.ReadUint16()
if err != nil { return err }
p.Flags = u16
p.Ciaddr, err = r.ReadIp4()
if err != nil { return err } if err != nil { return err }
p.Yiaddr, err = r.ReadIp4() p.Xid, err = nbr.ReadUint32()
if err != nil { return err } if err != nil { return err }
p.Siaddr, err = r.ReadIp4() p.Secs, err = nbr.ReadUint16()
if err != nil { return err } if err != nil { return err }
p.Gwaddr, err = r.ReadIp4() p.Flags, err = nbr.ReadUint16()
if err != nil { return err } if err != nil { return err }
err = r.ReadAllBytes(p.Chaddr[:]) p.Ciaddr, err = nbr.ReadIp4()
if err != nil { return err } if err != nil { return err }
err = r.ReadAllBytes(p.Sname[:]) p.Yiaddr, err = nbr.ReadIp4()
if err != nil { return err } if err != nil { return err }
err = r.ReadAllBytes(p.File[:]) p.Siaddr, err = nbr.ReadIp4()
if err != nil { return err } if err != nil { return err }
// magic p.Gwaddr, err = nbr.ReadIp4()
// options.. if err != nil { return err }
err = nbr.ReadBytesFull(p.Chaddr[:])
if err != nil { return err }
err = nbr.ReadBytesFull(p.Sname[:])
if err != nil { return err }
err = nbr.ReadBytesFull(p.File[:])
if err != nil { return err }
err = nbr.ReadBytesFull(cookie[:])
if err != nil {
if errors.Is(err, io.EOF) {
// TODO: no options
}
return err
}
if cookie != dhcp4_magic_cookie {
return fmt.Errorf("invalid magic cookie %v", cookie)
}
// load options
p.Options = make(map[Dhcp4OptCode][]byte)
for {
var oc uint8
var ol uint8
var optval [255]byte
oc, err = nbr.ReadByte() // option code
if err != nil {
if errors.Is(err, io.EOF) { break }
return err
}
if oc == DHCP4_OPT_PADDING { continue }
if oc == DHCP4_OPT_END { break }
ol, err = nbr.ReadByte() // option length
if err != nil { return err }
err = nbr.ReadBytesAtLeast(optval[:], int(ol))
if err != nil { return err }
// RFC 3396 mandates that when multiple occurrences of a concatenation-requiring
// option appear in a DHCPv4 packet, their data MUST be concatenated prior to
// processing. For non-concatenation-requiring (e.g. fixed-length) options, concatenation
// is not defined. but let's not make an exception and let the processor check
// validity.
p.Options[oc] = append(p.Options[oc], optval[:ol]...)
}
//for oc, ov = range p.Optons {
// ot = oc_to_type(oc)
// switch (ot) {
// case OT_UINT8:
// case OT_UINT16:
// case OT_UINT18:
// case OT_UINT32:
// case OT_IP4ADDR:
// case OT_IP46DDR:
// case OT_STRING:
// default:
// leave it as is
// }
//
//}
ov, ok = p.Options[DHCP4_OPT_MSG_TYPE]
if !ok { return fmt.Errorf("no message type") }
if len(ov) != 1 { return fmt.Errorf("invalid message type option") }
p.msg_type = ov[0]
*pkt = p *pkt = p
return nil return nil
@ -146,3 +220,7 @@ func (pkt *Dhcp4Pkt) Decode(b []byte) error {
func (pkt *Dhcp4Pkt) Encode() []byte { func (pkt *Dhcp4Pkt) Encode() []byte {
return nil return nil
} }
func (pkt *Dhcp4Pkt) MsgType() Dhcp4MsgType {
return pkt.msg_type
}

101
server.go
View File

@ -31,6 +31,7 @@ type DhcpServer struct {
p int // epoll p int // epoll
efd int // eventfd efd int // eventfd
pkt_chan chan[]byte
log Logger log Logger
} }
@ -104,6 +105,7 @@ func NewDhcpServer(ctx context.Context, name string, logger Logger) (*DhcpServer
s.p = p s.p = p
s.conns = make(map[string]DhcpConn, 0) s.conns = make(map[string]DhcpConn, 0)
s.conns_by_fd = make(map[int]DhcpConn, 0) s.conns_by_fd = make(map[int]DhcpConn, 0)
s.pkt_chan = make(chan[]byte, 256)
runtime.SetFinalizer(s, finalize_dhcp_server) runtime.SetFinalizer(s, finalize_dhcp_server)
return s, nil return s, nil
@ -154,9 +156,9 @@ func (s *DhcpServer) RunTask(wg *sync.WaitGroup) {
go s.run_handlers(&l_wg) go s.run_handlers(&l_wg)
} }
*/ */
fmt.Printf ("beginning of recv loop\n") s.log.Write("", LOG_INFO, "Receiver loop started")
s.run_recv_loop() s.run_recv_loop()
fmt.Printf ("end of recv loop\n") s.log.Write("", LOG_INFO, "Receiver loop ended")
// l_wg.Wait() // l_wg.Wait()
finalize_dhcp_server(s) finalize_dhcp_server(s)
@ -168,6 +170,7 @@ func (s *DhcpServer) ReqStop() {
// eventfd needs an 8-byte integer. // eventfd needs an 8-byte integer.
v = 1 v = 1
unix.Write(s.efd, (*[8]byte)(unsafe.Pointer(&v))[:]) unix.Write(s.efd, (*[8]byte)(unsafe.Pointer(&v))[:])
s.CtxCancel()
} }
} }
@ -256,13 +259,92 @@ func get_offsets(frame []byte) (int, int, int, int, int, error) {
return eth_off, ip_off, udp_off, dhcp_off, dhcp_end, nil return eth_off, ip_off, udp_off, dhcp_off, dhcp_end, nil
} }
func (s *DhcpServer) dhcp4_discover(pkt *Dhcp4Pkt) {
fmt.Printf("discover...\n")
/*
ans = NewDhcp4Pkt(
Dhcp4OptMsgType(HDCP4_MSG_OFFER),
Dhcp4Ciaddr("192.168.2.3")
)
*/
//Send(ans)
}
func (s *DhcpServer) dhcp4_request(pkt *Dhcp4Pkt) {
fmt.Printf("request...\n")
}
func (s *DhcpServer) dhcp4_release(pkt *Dhcp4Pkt) {
fmt.Printf("release...\n")
}
func (s *DhcpServer) dhcp4_inform(pkt *Dhcp4Pkt) {
fmt.Printf("inform...\n")
}
func (s *DhcpServer) dhcp4_lease_query(pkt *Dhcp4Pkt) {
fmt.Printf("lease query...\n")
}
// TODO:bulk lease query, etc
func (s *DhcpServer) dhcp4_packet_proc(wg *sync.WaitGroup, id int) {
var buf []byte
var err error
defer wg.Done()
s.log.Write("", LOG_INFO, "dhcp4 packet processor(%d) started", id)
proc_loop:
for {
select {
case <- s.Ctx.Done():
break proc_loop
case buf = <- s.pkt_chan:
var pkt Dhcp4Pkt
//TODO: ethernet part???
err = pkt.Decode(buf)
if err != nil { continue }
switch pkt.MsgType() {
case DHCP4_MSG_DISCOVER:
s.dhcp4_discover(&pkt)
case DHCP4_MSG_REQUEST:
s.dhcp4_request(&pkt)
case DHCP4_MSG_RELEASE:
s.dhcp4_release(&pkt)
case DHCP4_MSG_INFORM:
s.dhcp4_inform(&pkt)
case DHCP4_MSG_LEASE_QUERY:
s.dhcp4_lease_query(&pkt)
default:
// do nothing. ignore other types?
// TODO: logging?
}
//fmt.Printf("[%d] PKT: %+v\n", id, pkt)
}
}
s.log.Write("", LOG_INFO, "dhcp4 packet processor(%d) ended", id)
}
func (s *DhcpServer) run_recv_loop() error { func (s *DhcpServer) run_recv_loop() error {
var buf [1500]byte var buf [1500]byte
var nevts int var nevts int
var i int var i int
var evts [128]unix.EpollEvent var evts [128]unix.EpollEvent
var l_wg sync.WaitGroup
var err error var err error
for i = 0; i < 10; i++ { // how many go rootines would be the best?
l_wg.Add(1)
go s.dhcp4_packet_proc(&l_wg, i)
}
epoll_loop: epoll_loop:
for { for {
nevts, err = unix.EpollWait(s.p, evts[:], -1) nevts, err = unix.EpollWait(s.p, evts[:], -1)
@ -297,15 +379,11 @@ epoll_loop:
if err != nil { if err != nil {
fmt.Printf ("PACKET -> %s\n", err.Error()) fmt.Printf ("PACKET -> %s\n", err.Error())
} else { } else {
//fmt.Printf (">>%d [%s]<<\n", epos - spos, string(buf[spos: epos])) //var pkt Dhcp4Pkt
if string(buf[spos:epos]) == "quit\n" { //TODO: ethernet part???
break epoll_loop //pkt.Decode(buf[spos:epos])
} //fmt.Printf("%+v\n", pkt)
s.pkt_chan <- buf[spos:epos]
var pkt Dhcp4Pkt
pkt.Decode(buf[spos:epos])
fmt.Printf("%+v\n", pkt)
} }
} }
@ -319,5 +397,6 @@ epoll_loop:
} }
// if there are subtasks wait here for termination and close the dockets // if there are subtasks wait here for termination and close the dockets
l_wg.Wait()
return nil return nil
} }